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
Endpoints
POST /audio/auto POST
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"
}
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 |
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.
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.
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 —
ready, paywalled, processing, queued,
click_to_create, unavailable, or error. See
Response status for the full table.
GET /audio/auto/check GET
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, '');
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
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));
}
}
}