Developer API
EnneonRadar media API v1
A REST API for downloading public videos from TikTok, Instagram, Facebook and YouTube. Credit-metered, key-authenticated, and deliberately without a free tier.
€0.10 per download. No subscription, no minimum, no per-seat fee — the same credits and the same rate as the website, drawn from one balance. Pricing.
Base URL
https://enneonradar.online/api/v1/
Authentication
Send the key as a bearer token. A key looks like enrdl_XXXXXXXX_<secret>; the
enrdl_XXXXXXXX prefix is its id, safe to log, and the whole value is stored only as
a SHA-256 digest — we cannot show it to you again after creation.
Authorization: Bearer enrdl_XXXXXXXX_<secret>
There is no query-string token and no anonymous access. Requests without a valid key are
answered 401 invalid_api_key.
Scopes
media-jobs:writemedia-jobs:readcredits:read
A key is issued with all three by default; narrow them when you create it.
Rate limits
Three actor classes are limited separately. Current values, read live from configuration:
| Limit | Per minute | Per day |
|---|---|---|
| API requests (any endpoint) | 60 | 5000 |
| Job creation | 20 | 2000 |
A key may also carry its own monthly request quota. Exceeding a limit returns
429 with rate_limited or monthly_limit_reached and a
Retry-After header.
Credits
One successfully completed download costs one credit. A failed job, an invalid or blocked link and an unsupported source cost nothing. Validation does not change the balance; one credit is charged only after the resolver confirms usable media, before the file is fetched. Any later failure is refunded automatically through the append-only ledger.
Endpoints
POST /api/v1/media-jobs/
Submit a public video URL. Requires media-jobs:write.
curl -X POST https://enneonradar.online/api/v1/media-jobs/ \
-H "Authorization: Bearer $ENNEONRADAR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-unique-id-1" \
-d '{"url": "https://www.tiktok.com/@someone/video/1234567890"}'
202 Accepted
{
"job_id": "8f14e45f-ea0f-4b0e-9c1a-1d2f3a4b5c6d",
"status": "validating",
"platform": "tiktok",
"platform_label": "TikTok",
"source_url": "https://tiktok.com/@someone/video/1234567890",
"canonical_url": "https://tiktok.com/@someone/video/1234567890",
"created_at": "2026-09-09T10:00:00+00:00",
"credit": {"state": "none", "charged": false, "refunded": false},
"credit_balance": 100,
"duplicate": false
}
Idempotency-Key is honoured: a retry with the same key returns the original job
with duplicate: true and 200 OK, and is not charged again. Submitting
a URL you already have a live job for behaves the same way.
GET /api/v1/media-jobs/{job_id}/
Poll a job. Requires media-jobs:read. A temporary download_url is
included as soon as the job is ready, so one poll loop is enough.
{
"job_id": "8f14e45f-ea0f-4b0e-9c1a-1d2f3a4b5c6d",
"status": "ready",
"platform": "tiktok",
"ready_at": "2026-09-09T10:00:07+00:00",
"expires_at": "2026-09-10T10:00:07+00:00",
"media": {
"filename": "enneonradar-tiktok-8f14e45f.mp4",
"content_type": "video/mp4",
"size_bytes": 3874112,
"title": "…",
"author": "…",
"duration_seconds": 27
},
"formats": [],
"download_url": "https://…r2.cloudflarestorage.com/…?X-Amz-Signature=…",
"credit": {"state": "spent", "charged": true, "refunded": false},
"credit_balance": 99
}
Statuses are validating, preparing, ready,
failed and expired. A failed job carries an
error object with a stable code.
GET /api/v1/media-jobs/{job_id}/download/
Mint a fresh signed URL for a finished job — useful when the previous one has expired but the
file has not. Requires media-jobs:read. Add ?redirect=1 to receive a
302 to the file instead of JSON.
{
"job_id": "8f14e45f-…",
"download_url": "https://…",
"expires_in_seconds": 900,
"filename": "enneonradar-tiktok-8f14e45f.mp4",
"content_type": "video/mp4",
"size_bytes": 3874112,
"file_expires_at": "2026-09-10T10:00:07+00:00"
}
Signed URLs last 900 seconds. The file itself is deleted
24 hours after the job finished, after which this endpoint
returns 410 file_expired.
GET /api/v1/credits/
Your balance. Requires credits:read.
{
"credits": 99,
"breakdown": {"premium_allocation": 99, "purchased": 0},
"premium_monthly_allowance": 100,
"allocation_expires_at": "2026-10-01T00:00:00+00:00",
"packs": [{"code": "downloads-starter", "name": "Starter download pack", "credits": 50,
"price": {"amount_minor": 500, "currency": "EUR",
"formatted": "€5.00"},
"credits_expire_after_days": null}]
}
The two halves behave differently: the Premium allocation expires with the calendar month, purchased credits do not. Perishable credits are always spent first.
POST /api/v1/api-keys/ and DELETE /api/v1/api-keys/{key_id}/
Key management is authenticated by a logged-in browser session, not by an API
key — a key that could mint more keys is a far worse thing to leak. Both are CSRF-protected;
use the form on your downloads page.
{key_id} is the key's prefix. Revocation is idempotent.
GET /api/v1/platforms/
The supported-source list. The one endpoint that needs no key: it is the same information printed on this page and reveals nothing about any account.
Supported sources
| Platform | Key | Enabled | Hostnames |
|---|---|---|---|
| TikTok | tiktok |
yes | tiktok.com, vm.tiktok.com, vt.tiktok.com |
instagram |
yes | instagram.com, instagr.am | |
facebook |
yes | facebook.com, fb.watch, fb.com | |
| YouTube | youtube |
yes | youtube.com, youtu.be, m.youtube.com, music.youtube.com, youtube-nocookie.com |
Public URLs only, and a link must point at a single video: a profile, feed or search URL is
rejected with not_a_video_url. We never ask for a password, cookie or session, and
creator watermarks and attribution are left intact.
Error codes
Every failure returns the same envelope:
{"error": {"code": "no_credits", "message": "You have no download credits left. …"}}
| HTTP | Code | Means |
|---|---|---|
| 400 | url_required | No URL in the body. |
| 400 | url_malformed / url_too_long / url_not_http | The link cannot be parsed or is not http(s). |
| 400 | url_private_address | The link points at a private or local address. |
| 400 | unsupported_source | The hostname is not on the allowlist. |
| 400 | not_a_video_url | A profile or page link, not a single video. |
| 400 | provider_disabled | That platform is switched off. |
| 400 | media_unavailable | Private, deleted or region-locked. No credit charged. |
| 400 | media_too_large | Over the size or duration limit. |
| 401 | authentication_required / invalid_api_key / api_key_revoked | Key missing, wrong or revoked. |
| 402 | no_credits | Out of credits. Top up or subscribe. |
| 403 | scope_required | The key lacks the scope this endpoint needs. |
| 404 | not_found | No such job for this account. |
| 409 | not_ready | The job has not finished. Keep polling. |
| 409 | duplicate_job | A live job already exists for that link. |
| 409 | pending_validations | Available credits are reserved by validating jobs. Wait and retry. |
| 410 | file_expired | The file has been deleted. Submit the link again. |
| 429 | rate_limited / monthly_limit_reached / platform_limit_reached | Slow down; see Retry-After. |
| 502/503 | download_failed / resolver_unavailable / storage_unavailable | Our problem. Retried automatically, then refunded. |
| 503 | downloader_disabled | The downloader is turned off. |
Recommended integration
- Submit with an
Idempotency-Keyyou can reproduce, so a retry never double-charges. - Poll
GET /media-jobs/{job_id}/every two to three seconds; stop onready,failedorexpired. - Fetch the file from
download_urlpromptly — the link is short-lived and the file itself lasts 24 hours. - Treat
402 no_creditsas "buy credits", not as a retryable error. - Treat
409 pending_validationsas retryable after an existing validation finishes.
Need the pipeline built for you?
The API is self-serve and needs no conversation. If you would rather have the automation around it designed, built and run — scheduled publishing, cross-posting, collection and archiving, reporting — that is a service we offer: see social media automation, or email novend@enneontechnologies.com.
Also see pricing, the browser downloader, the terms and contact. Download only what you have the right to use.