AlefBot Public API
- API version:
v1
- Documentation version:
2026.02.21
- Last updated:
2026-02-21
- Base path:
/api/v1
All examples use /api/v1, which is the default deployment path. If your API_PREFIX is customized, prepend that prefix to every path.
Create upload slot, upload media bytes, then create transcription. Use one of these copy-paste examples:
API_KEY="alef_sk_live_your_key_here"
BASE_URL="https://alef-bot.top/api/v1"
UPLOAD_JSON=$(curl -s -X POST "$BASE_URL/uploads" -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d '{"filename":"audio.mp3","content_type":"audio/mpeg","size_bytes":1024}')
UPLOAD_ID=$(echo "$UPLOAD_JSON" | sed -n 's/.*"upload_id":"\([^"]*\)".*/\1/p')
curl -X PUT "$BASE_URL/uploads/$UPLOAD_ID/binary" -H "Authorization: Bearer $API_KEY" --data-binary @audio.mp3
curl -X POST "$BASE_URL/transcriptions" -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d "{"upload_id":"$UPLOAD_ID","output_format":"srt"}"
import requests
import time
API_KEY = "alef_sk_live_your_key_here"
BASE_URL = "https://alef-bot.top/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
FILE_PATH = "audio.mp3"
upload_res = requests.post(
f"{BASE_URL}/uploads",
headers=HEADERS,
json={"filename": "audio.mp3", "content_type": "audio/mpeg", "size_bytes": 1024}
).json()
with open(FILE_PATH, "rb") as f:
requests.put(upload_res["upload_url"], data=f)
job_res = requests.post(
f"{BASE_URL}/transcriptions",
headers=HEADERS,
json={
"upload_id": upload_res["upload_id"],
"output_format": "srt"
}
).json()
job_id = job_res["job_id"]
while True:
status_res = requests.get(f"{BASE_URL}/transcriptions/{job_id}", headers=HEADERS).json()
if status_res["status"] == "completed":
break
elif status_res["status"] == "failed":
print("Transcription failed")
exit(1)
time.sleep(5)
srt_content = requests.get(
f"{BASE_URL}/transcriptions/{job_id}/artifact?format=srt",
headers=HEADERS
).text
print(srt_content)
const fs = require('fs');
const API_KEY = "alef_sk_live_your_key_here";
const BASE_URL = "https://alef-bot.top/api/v1";
const HEADERS = { "Authorization": `Bearer ${API_KEY}` };
async function transcribe() {
const uploadRes = await fetch(`${BASE_URL}/uploads`, {
method: 'POST',
headers: { ...HEADERS, 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: "audio.mp3", content_type: "audio/mpeg", size_bytes: 1024 })
}).then(r => r.json());
const fileData = fs.readFileSync("audio.mp3");
await fetch(uploadRes.upload_url, { method: 'PUT', body: fileData });
const jobRes = await fetch(`${BASE_URL}/transcriptions`, {
method: 'POST',
headers: { ...HEADERS, 'Content-Type': 'application/json' },
body: JSON.stringify({
upload_id: uploadRes.upload_id,
output_format: "srt"
})
}).then(r => r.json());
const jobId = jobRes.job_id;
while (true) {
const statusRes = await fetch(`${BASE_URL}/transcriptions/${jobId}`, { headers: HEADERS }).then(r => r.json());
if (statusRes.status === "completed") break;
if (statusRes.status === "failed") throw new Error("Job failed");
await new Promise(resolve => setTimeout(resolve, 5000));
}
const srtContent = await fetch(`${BASE_URL}/transcriptions/${jobId}/artifact?format=srt`, { headers: HEADERS }).then(r => r.text());
console.log(srtContent);
}
transcribe();
const API_KEY = "alef_sk_live_your_key_here";
const BASE_URL = "https://alef-bot.top/api/v1";
const FILE_INPUT_SELECTOR = "#audio-file";
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function transcribeFromBrowserFile() {
const fileInput = document.querySelector(FILE_INPUT_SELECTOR);
const file = fileInput?.files?.[0];
if (!file) {
throw new Error("Select a file in an <input type=\"file\" id=\"audio-file\"> first.");
}
const authHeaders = { Authorization: "Bearer " + API_KEY };
const uploadRes = await fetch(BASE_URL + "/uploads", {
method: "POST",
headers: { ...authHeaders, "Content-Type": "application/json" },
body: JSON.stringify({
filename: file.name,
content_type: file.type || "audio/mpeg",
size_bytes: file.size,
}),
});
if (!uploadRes.ok) throw new Error("Failed creating upload slot: " + uploadRes.status);
const upload = await uploadRes.json();
const uploadBinaryRes = await fetch(upload.upload_url, {
method: "PUT",
headers: authHeaders,
body: file,
});
if (!uploadBinaryRes.ok) throw new Error("Failed uploading bytes: " + uploadBinaryRes.status);
const jobRes = await fetch(BASE_URL + "/transcriptions", {
method: "POST",
headers: { ...authHeaders, "Content-Type": "application/json" },
body: JSON.stringify({
upload_id: upload.upload_id,
output_format: "srt",
}),
});
if (!jobRes.ok) throw new Error("Failed creating job: " + jobRes.status);
const job = await jobRes.json();
while (true) {
const statusRes = await fetch(BASE_URL + "/transcriptions/" + job.job_id, { headers: authHeaders });
const status = await statusRes.json();
if (status.status === "completed") break;
if (status.status === "failed") throw new Error("Transcription failed");
await wait(5000);
}
const artifactRes = await fetch(BASE_URL + "/transcriptions/" + job.job_id + "/artifact?format=srt", {
headers: authHeaders,
});
if (!artifactRes.ok) throw new Error("Failed downloading artifact: " + artifactRes.status);
const srt = await artifactRes.text();
console.log(srt);
}
transcribeFromBrowserFile().catch(console.error);
- Send Authorization: Bearer <key> on every request.
- Accepted key prefixes: alef_sk_live_… and alef_sk_test_…
- Requests are rejected when the key is missing, invalid, revoked, mode-mismatched, or owned by a user without premium/platinum API access.
- Every /api/v1 response includes X-Request-ID for support and tracing.
Headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer token. Example: Bearer alef_sk_live_xxx |
- |
Recommended workflow
-
- POST /uploads to create an upload slot.
-
- PUT /uploads/{upload_id}/binary to upload media bytes.
-
- POST /transcriptions to create a job.
-
- Poll GET /transcriptions/{job_id} or consume webhooks for completion.
-
- GET /transcriptions/{job_id}/artifact?format=srt to retrieve output.
Rate limits
- Tier-based limits: premium = 30 requests/minute, platinum = 60 requests/minute.
- Rate-limited endpoints return X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
- When exceeded, the API returns 429 with code rate_limit_exceeded.
Idempotency (Optional)
- POST /uploads and POST /transcriptions support the optional Idempotency-Key header.
- If omitted, requests execute normally and bypass idempotency cache.
- If provided, retries with same body are deduplicated for 24 hours; mismatched bodies return 409.
Audio analysis behavior
- Language, dialect, and content type are detected automatically from audio in live processing.
- The public transcription request does not accept manual language/content-type override fields.
Artifact format selection
- GET /transcriptions/{job_id}/artifact supports
?format=srt|txt|docx (plain_text alias resolves to txt).
- GET /transcriptions/{job_id}/artifact.srt (or .txt/.docx) is also supported.
- Accept header content negotiation is preserved as a fallback.
Webhook security
- Webhook targets are validated to block SSRF-prone hosts (private, loopback, link-local, multicast, reserved, unspecified).
- Webhook delivery signs payloads using HMAC-SHA256 with headers X-AlefBot-Timestamp and X-AlefBot-Signature.
Error model
Error response shape
| Field |
Type |
Required |
Description |
Default |
error.code |
string |
Yes |
Stable machine-readable error code. |
- |
error.message |
string |
Yes |
Human-readable message. |
- |
error.request_id |
string |
null |
Yes |
Request tracing identifier. |
error.details |
object |
Yes |
Context object, may be empty. |
- |
{
"error": {
"code": "missing_api_key",
"message": "missing api key",
"request_id": "req_0adf4f750ef34d58ab",
"details": {}
}
}
Error handling matrix
| HTTP |
Code |
Description |
Resolution |
Docs |
| 400 |
invalid_webhook_url |
Webhook URL is malformed or DNS resolution fails. |
Ensure HTTPS URL is reachable from public internet and hostname resolves. |
Open |
| 400 |
webhook_url_forbidden_target |
Webhook target resolves to a blocked network range. |
Use a public DNS hostname/IP (no localhost/private/link-local/reserved ranges). |
Open |
| 400 |
unsupported_artifact_format |
Artifact format is not one of srt/txt/docx. |
Set `format=srt |
txt |
| 401 |
missing_api_key |
Authorization header is missing or not Bearer. |
Send Authorization: Bearer alef_sk_live_... on every request. |
Open |
| 401 |
invalid_api_key |
API key is invalid or revoked. |
Rotate/recreate key and verify key hash matches active key in dashboard. |
Open |
| 401 |
invalid_api_key_mode |
Key mode and prefix are inconsistent. |
Use alef_sk_live_... for live keys and alef_sk_test_... for test keys. |
Open |
| 401 |
invalid_api_key_user |
API key is linked to a user that does not exist. |
Recreate the API key from the dashboard and retry. |
- |
| 403 |
scope_violation_read_only |
Read-only key attempted a write operation. |
Use a full_access key for POST/PUT operations. |
Open |
| 403 |
plan_downgraded_api_revoked |
Owner account no longer has API access. |
Upgrade account tier to premium/platinum or use an eligible account. |
Open |
| 404 |
upload_not_found |
Upload slot does not exist for this account. |
Create a new upload slot with POST /uploads and use returned upload_id. |
Open |
| 410 |
upload_slot_expired |
Upload slot expired before upload/transcription submission. |
Create a new upload slot and retry the flow. |
- |
| 400 |
empty_upload_body |
Binary upload request body is empty. |
Send raw media bytes in the PUT body. |
- |
| 400 |
upload_size_mismatch |
Uploaded byte length does not match declared size_bytes. |
Send the exact file bytes and ensure size_bytes matches the payload. |
- |
| 404 |
job_not_found |
Job does not exist for this account. |
Verify job_id comes from this API key owner and has not been deleted. |
Open |
| 409 |
idempotency_mismatch |
Idempotency key reused with a different request body. |
Reuse key only for byte-identical retries or send a new key. |
Open |
| 409 |
upload_not_available |
Transcription was requested before media bytes were uploaded. |
PUT binary data to upload slot before POST /transcriptions. |
Open |
| 409 |
artifact_not_ready |
Artifact requested before job completed. |
Poll GET /transcriptions/{job_id} until status=completed. |
Open |
| 404 |
artifact_not_found |
No artifact available for this completed job. |
Retry with srt/txt/docx and verify output was generated successfully. |
Open |
| 429 |
rate_limit_exceeded |
Request limit exceeded for current minute window. |
Back off and retry after X-RateLimit-Reset timestamp. |
Open |
Validate X-AlefBot-Timestamp and X-AlefBot-Signature using your webhook secret:
import hashlib
import hmac
import json
def verify_alefbot_signature(body: dict, timestamp: str, signature: str, secret: str) -> bool:
payload = json.dumps(body, separators=(",", ":"), sort_keys=True)
expected = hmac.new(
secret.encode("utf-8"),
f"{timestamp}.{payload}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
const crypto = require("crypto");
function sortKeysDeep(value) {
if (Array.isArray(value)) return value.map(sortKeysDeep);
if (!value || typeof value !== "object") return value;
return Object.keys(value)
.sort()
.reduce((acc, key) => {
acc[key] = sortKeysDeep(value[key]);
return acc;
}, {});
}
function verifyAlefbotSignature(body, timestamp, signature, secret) {
const payload = JSON.stringify(sortKeysDeep(body));
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${payload}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
async function verifyAlefbotSignature(body, timestamp, signatureHex, secret) {
const canonical = JSON.stringify(sortKeysDeep(body));
const data = new TextEncoder().encode(timestamp + "." + canonical);
const keyData = new TextEncoder().encode(secret);
const cryptoKey = await crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signed = await crypto.subtle.sign("HMAC", cryptoKey, data);
const expectedHex = [...new Uint8Array(signed)]
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
return timingSafeEqualHex(expectedHex, signatureHex);
}
function sortKeysDeep(value) {
if (Array.isArray(value)) return value.map(sortKeysDeep);
if (!value || typeof value !== "object") return value;
return Object.keys(value)
.sort()
.reduce((acc, key) => {
acc[key] = sortKeysDeep(value[key]);
return acc;
}, {});
}
function timingSafeEqualHex(a, b) {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i += 1) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
return diff === 0;
}
- Interactive OpenAPI UI:
/developers/api-reference
| Method |
Path |
Purpose |
| POST |
/api/v1/keys |
Create a new public API key for the authenticated account. |
| GET |
/api/v1/keys |
List all public API keys for the authenticated user. |
| GET |
/api/v1/account |
Read account tier and wallet balance before job submission. |
| POST |
/api/v1/uploads |
Create an upload slot and return an upload_id plus binary upload URL. |
| PUT |
/api/v1/uploads/{upload_id}/binary |
Upload raw media bytes to an existing upload slot. |
| POST |
/api/v1/transcriptions |
Create a transcription job from a previously uploaded file. |
| GET |
/api/v1/transcriptions |
List API-origin transcription jobs with cursor-style pagination. |
| GET |
/api/v1/transcriptions/{job_id} |
Read a single API-origin transcription job and track status progression. |
| GET |
/api/v1/transcriptions/{job_id}/artifact |
Download output artifact for completed jobs. |
| POST |
/api/v1/webhooks/test |
Send a signed dummy webhook payload to validate endpoint reachability and signature verification. |
Create a new public API key for the authenticated account.
- Authentication: Requires a valid existing API key. Caller key must have full_access scope.
- Rate limiting: No route-level rate limiter.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Body fields
| Field |
Type |
Required |
Description |
Default |
name |
string |
Yes |
Human-readable key label. |
- |
scope |
string |
No |
Key permissions. Allowed: full_access, read_only. |
full_access |
mode |
string |
No |
Runtime mode. Allowed: live, test. |
live |
Request notes
scope=read_only blocks non-GET requests when that key is used.
mode=test creates sandbox behavior (immediate completion path in transcription flow).
- Returned key value is shown once. Persist it securely.
Success response
- HTTP 200: New key generated and persisted.
Success body fields
| Field |
Type |
Required |
Description |
Default |
key |
string |
Yes |
Full secret key value (only returned here). |
- |
item.id |
string |
Yes |
Key record id. |
- |
item.name |
string |
Yes |
Configured display name. |
- |
item.scope |
string |
Yes |
Configured scope. |
- |
item.mode |
string |
Yes |
Configured mode. |
- |
item.last_four_chars |
string |
Yes |
Last 4 characters for key identification. |
- |
item.is_valid |
boolean |
Yes |
Current validity state. |
- |
item.created_at |
datetime |
Yes |
Key creation timestamp (UTC). |
- |
Error responses
| HTTP |
Code |
Description |
| 401 |
invalid_api_key |
Missing/invalid caller key. |
| 403 |
scope_violation |
Caller key is not full_access. |
Examples
curl -X POST https://alef-bot.top/api/v1/keys \
-H "Authorization: Bearer alef_sk_live_primary" \
-H "Content-Type: application/json" \
-d '{
"name": "CI Read Only",
"scope": "read_only",
"mode": "test"
}'
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.post(
f"{BASE_URL}/keys",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"name": "CI Read Only",
"scope": "read_only",
"mode": "test",
},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createKey() {
const response = await fetch(BASE_URL + "/keys", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "CI Read Only",
scope: "read_only",
mode: "test",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
createKey().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createKey() {
const response = await fetch(BASE_URL + "/keys", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "CI Read Only",
scope: "read_only",
mode: "test",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
createKey().catch(console.error);
List all public API keys for the authenticated user.
- Authentication: Requires a valid API key (any scope).
- Rate limiting: No route-level rate limiter.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Success response
- HTTP 200: Array of key records.
Success body fields
| Field |
Type |
Required |
Description |
Default |
[].id |
string |
Yes |
Key record id. |
- |
[].name |
string |
Yes |
Key display name. |
- |
[].scope |
string |
Yes |
Key scope: full_access or read_only. |
- |
[].mode |
string |
Yes |
Key mode: live or test. |
- |
[].last_four_chars |
string |
Yes |
Last 4 characters for key identification. |
- |
[].is_valid |
boolean |
Yes |
Validity status. |
- |
[].created_at |
datetime |
Yes |
Key creation timestamp (UTC). |
- |
Error responses
| HTTP |
Code |
Description |
| 401 |
invalid_api_key |
Missing/invalid key. |
Examples
curl https://alef-bot.top/api/v1/keys \
-H "Authorization: Bearer alef_sk_live_primary"
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.get(
f"{BASE_URL}/keys",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function listKeys() {
const response = await fetch(BASE_URL + "/keys", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
listKeys().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function listKeys() {
const response = await fetch(BASE_URL + "/keys", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
listKeys().catch(console.error);
Read account tier and wallet balance before job submission.
- Authentication: Requires a valid API key.
- Rate limiting: No route-level rate limiter.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Success response
- HTTP 200: Current account state for API usage.
Success body fields
| Field |
Type |
Required |
Description |
Default |
account_id |
string |
Yes |
User id. |
- |
effective_tier |
string |
Yes |
Effective billing tier (premium/platinum). |
- |
api_access |
boolean |
Yes |
Whether this account can access the public API. |
- |
wallet_balance |
number |
Yes |
Current credits balance. |
- |
status |
string |
Yes |
Account API status. |
- |
Error responses
| HTTP |
Code |
Description |
| 401 |
invalid_api_key |
Missing/invalid key. |
Examples
curl https://alef-bot.top/api/v1/account \
-H "Authorization: Bearer alef_sk_live_primary"
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.get(
f"{BASE_URL}/account",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function getAccountContext() {
const response = await fetch(BASE_URL + "/account", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
getAccountContext().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function getAccountContext() {
const response = await fetch(BASE_URL + "/account", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
getAccountContext().catch(console.error);
Create an upload slot and return an upload_id plus binary upload URL.
- Authentication: Requires a full_access key.
- Rate limiting: Rate limited by tier.
- Idempotency: Optional but recommended.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Idempotency-Key |
string |
No |
Deduplicate retries for 24h. |
- |
Content-Type |
application/json |
Yes |
JSON payload. |
- |
Body fields
| Field |
Type |
Required |
Description |
Default |
filename |
string |
Yes |
Original media filename. |
- |
content_type |
string |
Yes |
MIME type. |
- |
size_bytes |
integer |
Yes |
Media file size in bytes. |
- |
Success response
- HTTP 201: Upload slot created, expires in 30 minutes.
Success body fields
| Field |
Type |
Required |
Description |
Default |
upload_id |
string |
Yes |
Upload slot identifier. |
- |
upload_url |
string |
Yes |
Relative path for binary PUT. |
- |
upload_method |
string |
Yes |
Binary upload method (PUT). |
- |
expires_at |
datetime |
Yes |
Upload slot expiration timestamp (UTC). |
- |
Error responses
| HTTP |
Code |
Description |
| 403 |
scope_violation_read_only |
Read-only key cannot create upload slots. |
| 409 |
idempotency_mismatch |
Idempotency key reused with different body. |
| 429 |
rate_limit_exceeded |
Minute rate limit exceeded. |
Examples
curl -X POST https://alef-bot.top/api/v1/uploads \
-H "Authorization: Bearer alef_sk_live_primary" \
-H "Content-Type: application/json" \
-d '{
"filename": "lecture.mp3",
"content_type": "audio/mpeg",
"size_bytes": 2048
}'
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.post(
f"{BASE_URL}/uploads",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"filename": "lecture.mp3",
"content_type": "audio/mpeg",
"size_bytes": 2048,
},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createUploadSlot() {
const response = await fetch(BASE_URL + "/uploads", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: "lecture.mp3",
content_type: "audio/mpeg",
size_bytes: 2048,
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
createUploadSlot().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createUploadSlot() {
const response = await fetch(BASE_URL + "/uploads", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: "lecture.mp3",
content_type: "audio/mpeg",
size_bytes: 2048,
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
createUploadSlot().catch(console.error);
Upload raw media bytes to an existing upload slot.
- Authentication: Requires a valid API key.
- Rate limiting: Rate limited by tier.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Path params
| Field |
Type |
Required |
Description |
Default |
upload_id |
string |
Yes |
Upload slot id returned by POST /uploads. |
- |
Request notes
- Body should contain the exact media bytes.
Success response
- HTTP 200: Upload acknowledged.
Success body fields
| Field |
Type |
Required |
Description |
Default |
ok |
boolean |
Yes |
Always true on success. |
- |
bytes_received |
integer |
Yes |
Actual bytes received by the API. |
- |
Error responses
| HTTP |
Code |
Description |
| 400 |
empty_upload_body |
Request body is empty. |
| 400 |
upload_size_mismatch |
Uploaded bytes count differs from declared size_bytes. |
| 404 |
upload_not_found |
Unknown upload_id. |
| 410 |
upload_slot_expired |
Upload slot has expired. |
| 429 |
rate_limit_exceeded |
Minute rate limit exceeded. |
Examples
curl -X PUT "https://alef-bot.top/api/v1/uploads/upl_02f4c48f7fc74ec58cc6/binary" \
-H "Authorization: Bearer alef_sk_live_primary" \
--data-binary @lecture.mp3
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
UPLOAD_ID = "upl_02f4c48f7fc74ec58cc6"
FILE_PATH = "lecture.mp3"
with open(FILE_PATH, "rb") as file_obj:
response = requests.put(
f"{BASE_URL}/uploads/{UPLOAD_ID}/binary",
headers={"Authorization": f"Bearer {API_KEY}"},
data=file_obj,
timeout=120,
)
response.raise_for_status()
print(response.json())
const fs = require("node:fs");
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const UPLOAD_ID = "upl_02f4c48f7fc74ec58cc6";
async function uploadBinary() {
const fileBytes = fs.readFileSync("lecture.mp3");
const response = await fetch(BASE_URL + "/uploads/" + UPLOAD_ID + "/binary", {
method: "PUT",
headers: { Authorization: "Bearer " + API_KEY },
body: fileBytes,
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
uploadBinary().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const UPLOAD_ID = "upl_02f4c48f7fc74ec58cc6";
async function uploadBinaryFromBrowser() {
const fileInput = document.querySelector("#audio-file");
const file = fileInput?.files?.[0];
if (!file) throw new Error("Select a file in <input type=\"file\" id=\"audio-file\">.");
const response = await fetch(BASE_URL + "/uploads/" + UPLOAD_ID + "/binary", {
method: "PUT",
headers: { Authorization: "Bearer " + API_KEY },
body: file,
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
uploadBinaryFromBrowser().catch(console.error);
Create a transcription job from a previously uploaded file.
- Authentication: Requires a full_access key.
- Rate limiting: Rate limited by tier.
- Idempotency: Optional but recommended.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Idempotency-Key |
string |
No |
Deduplicate retries for 24h. |
- |
Content-Type |
application/json |
Yes |
JSON payload. |
- |
Body fields
| Field |
Type |
Required |
Description |
Default |
upload_id |
string |
Yes |
Identifier from POST /uploads. |
- |
translate_to_hebrew |
boolean |
No |
Whether output should be translated to Hebrew. |
false |
model_tier |
string |
null |
No |
Model tier. Allowed values: standard, fast_low_cost, premium_quality, null. Premium Quality requires Basic or higher and costs 2x the selected output format. |
user_prompt |
string |
null |
No |
Optional prompt biasing. |
output_format |
string |
No |
Output format. Allowed values: srt, plain_text. |
srt |
webhook_url |
string |
null |
No |
Optional callback target. |
Request notes
- Language/content-type/dialect are inferred automatically by the pipeline in live mode.
- Test keys complete immediately and return status=completed with mock summary.
Success response
- HTTP 202: Job created (live keys return 202 queued; test keys return 200 completed).
Success body fields
| Field |
Type |
Required |
Description |
Default |
job_id |
string |
Yes |
Transcription job id. |
- |
status |
string |
Yes |
queued (live key) or completed (test key). |
- |
links.self |
string |
Yes |
Canonical job resource path. |
- |
Error responses
| HTTP |
Code |
Description |
| 403 |
scope_violation_read_only |
Read-only key cannot create transcription jobs. |
| 404 |
upload_not_found |
upload_id does not exist for this user. |
| 409 |
idempotency_mismatch |
Idempotency key reused with different body. |
| 409 |
upload_not_available |
Media bytes are not available in storage yet. |
| 410 |
upload_slot_expired |
Upload slot has expired. |
| 429 |
rate_limit_exceeded |
Minute rate limit exceeded. |
Examples
curl -X POST https://alef-bot.top/api/v1/transcriptions \
-H "Authorization: Bearer alef_sk_live_primary" \
-H "Content-Type: application/json" \
-d '{
"upload_id": "upl_02f4c48f7fc74ec58cc6",
"translate_to_hebrew": false,
"output_format": "srt"
}'
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.post(
f"{BASE_URL}/transcriptions",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"upload_id": "upl_02f4c48f7fc74ec58cc6",
"translate_to_hebrew": False,
"output_format": "srt",
},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createTranscription() {
const response = await fetch(BASE_URL + "/transcriptions", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
upload_id: "upl_02f4c48f7fc74ec58cc6",
translate_to_hebrew: false,
output_format: "srt",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
createTranscription().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function createTranscription() {
const response = await fetch(BASE_URL + "/transcriptions", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
upload_id: "upl_02f4c48f7fc74ec58cc6",
translate_to_hebrew: false,
output_format: "srt",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
createTranscription().catch(console.error);
List API-origin transcription jobs with cursor-style pagination.
- Authentication: Requires a valid API key that owns the jobs.
- Rate limiting: Rate limited by tier.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Query params
| Field |
Type |
Required |
Description |
Default |
limit |
integer |
No |
Page size (1-100). |
20 |
cursor |
integer |
No |
Zero-based offset cursor. |
0 |
status |
string |
No |
Optional status filter (pending |
processing |
Success response
- HTTP 200: Paginated transcription job list.
Success body fields
| Field |
Type |
Required |
Description |
Default |
items[].job_id |
string |
Yes |
Transcription job id. |
- |
items[].status |
string |
Yes |
Lowercase job status. |
- |
items[].created_at |
datetime |
Yes |
Creation timestamp (UTC). |
- |
items[].updated_at |
datetime |
Yes |
Last updated timestamp (UTC). |
- |
items[].media_duration_seconds |
number |
null |
Yes |
Media duration when available. |
items[].credits_used |
number |
null |
Yes |
Charged credits when available. |
items[].analysis.language |
string |
null |
Yes |
Detected language when available. |
items[].analysis.dialect |
string |
null |
Yes |
Detected dialect when available. |
items[].links.self |
string |
Yes |
Canonical job resource path. |
- |
items[].links.artifact |
string |
Yes |
Artifact download resource path. |
- |
total |
integer |
Yes |
Total number of matching API-origin jobs. |
- |
links.self |
string |
Yes |
Current page URL path. |
- |
links.next |
string |
null |
Yes |
Next page URL path when available. |
links.prev |
string |
null |
Yes |
Previous page URL path when available. |
Error responses
| HTTP |
Code |
Description |
| 429 |
rate_limit_exceeded |
Minute rate limit exceeded. |
Examples
curl "https://alef-bot.top/api/v1/transcriptions?limit=20&cursor=0" \
-H "Authorization: Bearer alef_sk_live_primary"
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.get(
f"{BASE_URL}/transcriptions",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"limit": 20, "cursor": 0},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function listTranscriptions() {
const params = new URLSearchParams({ limit: "20", cursor: "0" });
const response = await fetch(BASE_URL + "/transcriptions?" + params.toString(), {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
listTranscriptions().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function listTranscriptions() {
const params = new URLSearchParams({ limit: "20", cursor: "0" });
const response = await fetch(BASE_URL + "/transcriptions?" + params.toString(), {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
listTranscriptions().catch(console.error);
Read a single API-origin transcription job and track status progression.
- Authentication: Requires a valid API key that owns the job.
- Rate limiting: Rate limited by tier.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
If-None-Match |
string |
No |
Optional ETag for conditional GET (returns 304 when unchanged). |
- |
Path params
| Field |
Type |
Required |
Description |
Default |
job_id |
string |
Yes |
Transcription job id. |
- |
Success response
- HTTP 200: Current job snapshot.
Success body fields
| Field |
Type |
Required |
Description |
Default |
job_id |
string |
Yes |
Transcription job id. |
- |
status |
string |
Yes |
Lowercase job status. Common values: pending, processing, completed, failed, cancelled. |
- |
created_at |
datetime |
Yes |
Creation timestamp (UTC). |
- |
updated_at |
datetime |
Yes |
Last updated timestamp (UTC). |
- |
media_duration_seconds |
number |
null |
Yes |
Media duration when available. |
credits_used |
number |
null |
Yes |
Charged credits when available. |
analysis.language |
string |
null |
Yes |
Detected language when available. |
analysis.dialect |
string |
null |
Yes |
Detected dialect when available. |
links.self |
string |
Yes |
Canonical job resource path. |
- |
links.artifact |
string |
Yes |
Artifact download resource path. |
- |
Error responses
| HTTP |
Code |
Description |
| 404 |
job_not_found |
Job does not exist. |
Examples
curl https://alef-bot.top/api/v1/transcriptions/ac69835f-0575-4429-a95f-1fbc977bb4d3 \
-H "Authorization: Bearer alef_sk_live_primary"
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3"
response = requests.get(
f"{BASE_URL}/transcriptions/{JOB_ID}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3";
async function getTranscriptionStatus() {
const response = await fetch(BASE_URL + "/transcriptions/" + JOB_ID, {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
getTranscriptionStatus().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3";
async function getTranscriptionStatus() {
const response = await fetch(BASE_URL + "/transcriptions/" + JOB_ID, {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
getTranscriptionStatus().catch(console.error);
Download output artifact for completed jobs.
- Authentication: Requires a valid API key that owns the job.
- Rate limiting: No route-level rate limiter.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Path params
| Field |
Type |
Required |
Description |
Default |
job_id |
string |
Yes |
Transcription job id. |
- |
Query params
| Field |
Type |
Required |
Description |
Default |
format |
string |
No |
Requested format. Supported values: srt, txt, docx (plain_text alias is accepted). |
srt |
Request notes
- You can also use URL extension form:
/transcriptions/{job_id}/artifact.srt (or .txt, .docx).
Success response
- HTTP 200: Returns full artifact body in the requested format.
Success body fields
| Field |
Type |
Required |
Description |
Default |
body |
binary/text |
Yes |
Full artifact content body. |
- |
Error responses
| HTTP |
Code |
Description |
| 400 |
unsupported_artifact_format |
Unsupported artifact format. |
| 404 |
artifact_not_found |
No artifact exists for this completed job. |
| 404 |
job_not_found |
Job does not exist. |
| 409 |
artifact_not_ready |
Job is not completed yet. |
Examples
curl "https://alef-bot.top/api/v1/transcriptions/ac69835f-0575-4429-a95f-1fbc977bb4d3/artifact?format=srt" \
-H "Authorization: Bearer alef_sk_live_primary" \
--output result.srt
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3"
response = requests.get(
f"{BASE_URL}/transcriptions/{JOB_ID}/artifact",
params={"format": "srt"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
response.raise_for_status()
with open("result.srt", "wb") as file_obj:
file_obj.write(response.content)
print("Saved result.srt")
const fs = require("node:fs/promises");
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3";
async function downloadArtifact() {
const response = await fetch(BASE_URL + "/transcriptions/" + JOB_ID + "/artifact?format=srt", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
const content = await response.text();
await fs.writeFile("result.srt", content, "utf8");
console.log("Saved result.srt");
}
downloadArtifact().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
const JOB_ID = "ac69835f-0575-4429-a95f-1fbc977bb4d3";
async function downloadArtifact() {
const response = await fetch(BASE_URL + "/transcriptions/" + JOB_ID + "/artifact?format=srt", {
headers: { Authorization: "Bearer " + API_KEY },
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = downloadUrl;
anchor.download = "result.srt";
anchor.click();
URL.revokeObjectURL(downloadUrl);
}
downloadArtifact().catch(console.error);
Send a signed dummy webhook payload to validate endpoint reachability and signature verification.
- Authentication: Requires a full_access API key.
- Rate limiting: No route-level rate limiter.
- Idempotency: Not required.
Request headers
| Field |
Type |
Required |
Description |
Default |
Authorization |
string |
Yes |
Bearer API key. |
- |
Content-Type |
application/json |
Yes |
JSON payload. |
- |
Body fields
| Field |
Type |
Required |
Description |
Default |
webhook_url |
string |
Yes |
Webhook destination URL to test. |
- |
event_type |
string |
No |
Dummy event name. |
transcription.completed |
Success response
- HTTP 200: Webhook test dispatched with signed headers.
Success body fields
| Field |
Type |
Required |
Description |
Default |
ok |
boolean |
Yes |
Whether webhook endpoint returned 2xx. |
- |
event_id |
string |
Yes |
Generated test event id. |
- |
delivered |
boolean |
Yes |
Delivery success flag. |
- |
status_code |
integer |
null |
Yes |
Receiver HTTP status code when available. |
error |
string |
null |
Yes |
Network or response error details. |
Error responses
| HTTP |
Code |
Description |
| 403 |
scope_violation_read_only |
Read-only key cannot send webhook test events. |
| 400 |
invalid_webhook_url |
Malformed or unreachable URL. |
| 400 |
webhook_url_forbidden_target |
Target host is disallowed (SSRF protection). |
Examples
curl -X POST https://alef-bot.top/api/v1/webhooks/test \
-H "Authorization: Bearer alef_sk_live_primary" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://client.example.com/webhooks/alefbot",
"event_type": "transcription.completed"
}'
import requests
API_KEY = "alef_sk_live_primary"
BASE_URL = "https://alef-bot.top/api/v1"
response = requests.post(
f"{BASE_URL}/webhooks/test",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"webhook_url": "https://client.example.com/webhooks/alefbot",
"event_type": "transcription.completed",
},
timeout=30,
)
response.raise_for_status()
print(response.json())
const API_KEY = process.env.ALEFBOT_API_KEY ?? "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function sendWebhookTest() {
const response = await fetch(BASE_URL + "/webhooks/test", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
webhook_url: "https://client.example.com/webhooks/alefbot",
event_type: "transcription.completed",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status + " " + (await response.text()));
console.log(await response.json());
}
sendWebhookTest().catch(console.error);
const API_KEY = "alef_sk_live_primary";
const BASE_URL = "https://alef-bot.top/api/v1";
async function sendWebhookTest() {
const response = await fetch(BASE_URL + "/webhooks/test", {
method: "POST",
headers: {
Authorization: "Bearer " + API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
webhook_url: "https://client.example.com/webhooks/alefbot",
event_type: "transcription.completed",
}),
});
if (!response.ok) throw new Error("Request failed: " + response.status);
const data = await response.json();
console.log(data);
}
sendWebhookTest().catch(console.error);