Auto Audio

REST API

Call Auto Audio from your own code instead of the widget: authentication, endpoints, examples, and error handling.

Authentication

All API requests require authentication using a bearer token:

Authorization: Bearer YOUR_API_TOKEN
Contact Everlit support to obtain API credentials for your publication.

Endpoints

POST /audio/auto POST

POST https://creator.everlit.audio/audio/auto

Initiates audio conversion for a given URL. This endpoint is asynchronous and will either return a completed embed immediately (if cached), indicate processing is in progress, or start a new conversion job.

Request Body

{
  "url": "https://example.com/article",
  "publication_id": "pblc_abc123xyz",
  "conversation_mode": true,
  "sonic_optimizer": true,
  "client_version": "3.0.0"
}
Widget v2: everlitAutoAudio.js sends client_version (currently "3.0.0") with every POST. It is optional for your own integrations, but helpful to include so support can identify your client.

Required Parameters

Parameter Type Description
url string The URL of the article to convert
publication_id string Your publication ID (format: pblc_xxxxx)

Optional Parameters

Parameter Type Default Description
primary_voice_id string pub default Voice ID for primary narrator
guest_voice_id string pub default Voice ID for secondary narrator
conversation_mode boolean false Enable multi-voice conversation mode
sonic_optimizer boolean false Enable Audio Polish for enhanced quality
optimizer_level string "2" Audio Polish level: "0" (symbols/numbers only), "1" (light), "2" (default), "3" (heaviest rewrite). Any other value silently uses a conservative built-in prompt
language string null Language tag matching the keys of the publication's snrf_voices (en-US, es-MX, es). Skips detection: narrator voice, disclaimer text, disclaimer voice and the on-page disclaimer all resolve through this language, and it is passed to TTS as a language hint. A value that isn't a language tag is ignored
disable_language_detection boolean false Legacy alias for language: <publication default language> (snrf_default_language, or en when unset)
mix boolean true Run Creator’s mixer for this article. false (or "") means no intro/outro music. Music is always mixed by Creator, so the delivered audio is always a finished master — the player never adds music of its own
read_urls boolean false Read URLs found in content aloud
read_alt_text boolean false Read image alt text
intro_mixable_id string null Custom intro audio mixable ID. "shuffle" plays a random intro; "" turns the intro music off; null keeps the publication default (which shuffles a random intro when none is configured)
skip_intro_mixable boolean pub setting Play no intro music — the explicit form of intro_mixable_id: "". Defaults to the publication's snrf_skip_intro_mixable; naming an intro_mixable_id overrides it
shuffle_intro_mixable boolean pub setting Play a random intro from the publication's intro/outro music — the explicit form of intro_mixable_id: "shuffle", and what an unset intro already does
outro_mixable_id string null Custom outro audio mixable ID. "shuffle" plays a random outro; "" turns the outro music off; null keeps the publication default
skip_outro_mixable boolean pub setting Play no outro music — the explicit form of outro_mixable_id: "". Defaults to the publication's snrf_skip_outro_mixable; naming an outro_mixable_id overrides it
shuffle_outro_mixable boolean pub setting Play a random outro from the publication's intro/outro music — the explicit form of outro_mixable_id: "shuffle". Defaults to the publication's snrf_shuffle_outro_mixable; naming an outro_mixable_id overrides it. A track is picked per narration, and is not recorded
disclaimer string null Disclaimer text to read before content. "" silences it (the same flag suppress_ai_disclaimer: true sets); null uses the publication default. Never send the literal string "false" — it would be narrated aloud
suppress_ai_disclaimer boolean false Silence the spoken AI disclaimer for this article, overriding the publication's configured disclaimer
read_author_in_audio_enabled boolean true Read the author byline aloud in the audio
Blank vs null: for disclaimer, intro_mixable_id, outro_mixable_id and mix, an explicitly sent empty string ("") means turn this off for this article. The music beds take one more value: on intro_mixable_id, outro_mixable_id, intro_music and outro_music, an id plays that track, "shuffle" plays a random track, "" plays none, and null uses the publication default. null is identical to omitting the key entirely — for every parameter on this endpoint, aliases included — and means use the publication default. This applies to POST /audio/auto only.
Choosing the language yourself: send a language tag to skip detection. It is matched against the publication's per-language maps (snrf_voices, snrf_disclaimers, snrf_disclaimer_voices, snrf_disclaimer_ui_text) in one order: the exact regional tag, then the language family (es-MX/es-ES/es for Spanish, hi/in for Devanagari, in for other Indian languages), then the publication's snrf_default_language and its bare family code — or en-US then en when that setting is unset — then the literal default key, and finally the flat legacy settings. snrf_default_language also replaces the hardcoded en when detection is unreliable; unset, everything behaves exactly as before.

These friendly aliases are also accepted, each mapping to the canonical parameter (if both are sent, the canonical key wins): music → mix, intro_music → intro_mixable_id, outro_music → outro_mixable_id, audio_polish → sonic_optimizer, audio_polish_level → optimizer_level, podcast_mode → conversation_mode.

Full parameter reference: See the complete parameter table for all available options.

Response Examples

{
  "successful": true,
  "status": "ready",
  "embed": "<iframe src=\"https://everlit.audio/embeds/artl_abc123?eut=xyz\" frameborder=\"0\"></iframe>",
  "article_id": "artl_abc123",
  "player_url": "https://everlit.audio/embeds/artl_abc123",
  "metadata": {
    "title": "Article Title",
    "summary": "Article summary text",
    "duration": 245.5
  },
  "dom_query_params": null,
  "published_at": "2025-09-29T10:30:00Z",
  "duration": 245.5,
  "title": "Article Title",
  "summary": "Article summary text",
  "authors": ["Author Name"],
  "tags": ["category:News", "author:Author Name", "language:English"],
  "disclaimer_ui_text": "This audio was generated using AI"
}
{
  "successful": true,
  "status": "processing",
  "waiting": true,
  "article_id": "artl_abc123",
  "player_url": "https://everlit.audio/embeds/artl_abc123",
  "message": "Everlit audio conversion is still in progress."
}
{
  "successful": false,
  "status": "error",
  "reason": "Unable to fetch the article content."
}
Status field: every response carries a status — ready, paywalled, processing, queued, click_to_create, unavailable, or error. See Response status for the full table.

GET /audio/auto/check GET

GET https://creator.everlit.audio/audio/auto/check?evp=BASE64

Checks the status of audio conversion for a given URL. This is the recommended endpoint for polling conversion status, as it's lightweight and optimized for frequent checks.

Query Parameters

Parameter Type Description
evp string Base64-encoded JSON containing url, publication_id, and optional parameters

EVP Encoding

The evp parameter contains URL-safe base64-encoded JSON:

// Original object
const params = {
  url: "https://example.com/article",
  publication_id: "pblc_abc123xyz",
  eut: "optional_analytics_token",
  client_version: "3.0.0"   // sent by everlitAutoAudio.js v2; optional for your own code
};

// Encode to URL-safe base64
const evp = btoa(JSON.stringify(params))
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');
Note: GET requests will never initiate a new conversion - they only check existing status.

Code Examples

Complete JavaScript Implementation

async function convertToAudio(url, publicationId) {
  // Check current status
  const params = {
    url: url,
    publication_id: publicationId
  };

  const evp = btoa(JSON.stringify(params))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');

  const checkResponse = await fetch(
    `https://creator.everlit.audio/audio/auto/check?evp=${evp}`
  );
  const checkData = await checkResponse.json();

  if (checkData.embed) {
    return checkData.embed; // Already ready!
  }

  if (checkData.create || checkData.waiting) {
    // Initiate if needed
    if (checkData.create) {
      await fetch('https://creator.everlit.audio/audio/auto', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(params)
      });
    }

    // Poll for completion
    return await pollForCompletion(url, publicationId);
  }
}

async function pollForCompletion(url, publicationId, maxAttempts = 60) {
  for (let i = 0; i < maxAttempts; i++) {
    await new Promise(r => setTimeout(r, 5000)); // Wait 5s

    const params = { url, publication_id: publicationId };
    const evp = btoa(JSON.stringify(params))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');

    const response = await fetch(
      `https://creator.everlit.audio/audio/auto/check?evp=${evp}`
    );
    const data = await response.json();

    if (data.embed) return data.embed;
    if (!data.waiting) throw new Error(data.reason);
  }

  throw new Error('Conversion timeout');
}

// Usage
const embed = await convertToAudio(
  'https://example.com/article',
  'pblc_abc123xyz'
);
document.getElementById('player').innerHTML = embed;

cURL Commands

# Initiate conversion
curl -X POST https://creator.everlit.audio/audio/auto \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "url": "https://example.com/article",
    "publication_id": "pblc_abc123xyz",
    "conversation_mode": true,
    "sonic_optimizer": true
  }'

# Check status (encode the parameters first)
EVP=$(echo -n '{"url":"https://example.com/article","publication_id":"pblc_abc123xyz"}' | \
  base64 | tr '+/' '-_' | tr -d '=')

curl "https://creator.everlit.audio/audio/auto/check?evp=$EVP"

Python Implementation

import requests
import json
import base64
import time

class EverlitAutoAudio:
    BASE_URL = 'https://creator.everlit.audio'

    def __init__(self, publication_id, api_token=None):
        self.publication_id = publication_id
        self.api_token = api_token

    def convert_article(self, url, **options):
        # Check status
        status = self.check_status(url)
        if status.get('embed'):
            return status['embed']

        # Initiate if needed
        if status.get('create'):
            self.initiate_conversion(url, **options)

        # Poll for completion
        return self.poll_for_completion(url)

    def check_status(self, url):
        params = {'url': url, 'publication_id': self.publication_id}
        evp = base64.urlsafe_b64encode(
            json.dumps(params).encode()
        ).decode().rstrip('=')

        response = requests.get(
            f'{self.BASE_URL}/audio/auto/check',
            params={'evp': evp}
        )
        return response.json()

    def initiate_conversion(self, url, **options):
        headers = {'Content-Type': 'application/json'}
        if self.api_token:
            headers['Authorization'] = f'Bearer {self.api_token}'

        payload = {
            'url': url,
            'publication_id': self.publication_id,
            **options
        }

        response = requests.post(
            f'{self.BASE_URL}/audio/auto',
            headers=headers,
            json=payload
        )
        return response.json()

    def poll_for_completion(self, url, max_attempts=60):
        for _ in range(max_attempts):
            time.sleep(5)
            status = self.check_status(url)
            if status.get('embed'):
                return status['embed']
        raise Exception('Conversion timeout')

# Usage
client = EverlitAutoAudio('pblc_abc123xyz', 'your_token')
embed = client.convert_article(
    'https://example.com/article',
    conversation_mode=True,
    sonic_optimizer=True
)
print(embed)

Ruby Implementation

require 'net/http'
require 'json'
require 'base64'

class EverlitAutoAudio
  BASE_URL = 'https://creator.everlit.audio'

  def initialize(publication_id, api_token = nil)
    @publication_id = publication_id
    @api_token = api_token
  end

  def convert_article(url, options = {})
    status = check_status(url)
    return status[:embed] if status[:embed]

    initiate_conversion(url, options) if status[:create]
    poll_for_completion(url)
  end

  def check_status(url)
    params = { url: url, publication_id: @publication_id }
    evp = Base64.urlsafe_encode64(params.to_json, padding: false)

    uri = URI("#{BASE_URL}/audio/auto/check?evp=#{evp}")
    response = Net::HTTP.get_response(uri)
    JSON.parse(response.body, symbolize_names: true)
  end

  def initiate_conversion(url, options = {})
    uri = URI("#{BASE_URL}/audio/auto")
    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['Authorization'] = "Bearer #{@api_token}" if @api_token

    body = { url: url, publication_id: @publication_id }.merge(options)
    request.body = body.to_json

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body, symbolize_names: true)
  end

  def poll_for_completion(url, max_attempts = 60)
    max_attempts.times do
      sleep(5)
      status = check_status(url)
      return status[:embed] if status[:embed]
    end
    raise "Conversion timeout"
  end
end

# Usage
client = EverlitAutoAudio.new('pblc_abc123xyz', 'your_token')
embed = client.convert_article(
  'https://example.com/article',
  conversation_mode: true,
  sonic_optimizer: true
)
puts embed

Error Handling

Common Error Scenarios

Scenario Response Action
Missing URL {"successful": false, "message": "No URL was sent..."} Provide valid URL parameter
Rate Limited {"successful": true, "reason": "Everlit is already hard at work..."} Wait 30+ seconds and retry
URL Blocked {"successful": false, "reason": "This URL is not available..."} Check whitelist settings
Monthly Limit 403: {"error": "Monthly article limit reached"} Upgrade plan or wait for reset
Invalid Voice {"successful": false, "reason": "Invalid parameters: voice_id..."} Use valid voice ID for your publisher
Extraction Failed {"successful": false, "reason": "Unable to fetch article..."} Check URL accessibility and format

Retry Strategy

Recommended: Implement exponential backoff for transient errors (5s, 10s, 20s, 40s) with max 5 attempts.
async function withRetry(fn, maxRetries = 5) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      const delay = Math.pow(2, i) * 1000; // Exponential backoff
      await new Promise(r => setTimeout(r, delay));
    }
  }
}