Downora

Reference

Downora API

One endpoint: give it an Envato Elements or Freepik asset and it returns a direct download URL. You are charged only for a request that produced a downloadUrl — if it did not, the charge is $0.

Quickstart

  1. 1. Sign in, then create a key on API Keys. The full secret (prefixed dn_live_) is shown exactly once.
  2. 2. Top up your USD balance on Billing.
  3. 3. Call the endpoint below with your key and an asset URL.
curl
curl -X POST https://your-api.downora.app/v1/download \
  -H "X-API-Key: dn_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "platform": "freepik",
    "url": "https://www.freepik.com/free-vector/example_16172815.htm"
  }'

Base URL: https://your-api.downora.app

Authentication

There are two credential types and they map to two different billing sources. Send one or the other, never both.

API source

Server-to-server. Uses your API key and is always billed against your USD balance at the API price — the free daily quota does not apply.

X-API-Key: dn_live_your_key_here

Keep API keys server-side. Anyone holding the key can spend your balance, and a leaked key can only be revoked, never rotated in place.

POST /v1/download

Supply platform plus either url (an asset page link) or assetId. Valid platforms are freepik and envato-elements.

FieldTypeRequiredNotes
platformstringYesfreepik | envato-elements
urlstringEitherAsset page link on an allowed host for that platform.
Request
POST https://your-api.downora.app/v1/download
X-API-Key: dn_live_your_key_here
Content-Type: application/json
Idempotency-Key: 3f9c1c8e-6b1a-4f2b-9a77-1c2d3e4f5a6b

{
  "platform": "envato-elements",
  "url": "https://elements.envato.com/some-asset-ABC123"
}
Response200 OK
{
  "success": true,
  "requestId": "req_01J8Z9K2M4N6P8Q0R2S4T6",
  "provider": "primary",
  "assetId": "ABC123",
  "downloadUrl": "https://…signed-url…",
  "licenceUrl": "https://…licence-pdf…",
  "billing": {
    "type": "paid",
    "amount": "0.025000",
    "currency": "USD"
  }
}

This response is flat — there is no data wrapper. Money values are decimal strings; parse them only for display. billing.type is free (covered by the daily quota), paid (drawn from your balance) or none.

downloadUrl and licenceUrl are short-lived and returned only here — they are never stored and cannot be re-read from your usage history. Download the file straight away.

Error response402 Payment Required
{
  "success": false,
  "requestId": "req_01J8Z9K2M4N6P8Q0R2S4T6",
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Your balance does not cover this download."
  }
}

Idempotency

Send an Idempotency-Key header (a UUID you generate) on every download. If a network blip makes you retry, the worker replays the cached response instead of running a second download — so a retry cannot double-charge you.

Idempotency-Key: 3f9c1c8e-6b1a-4f2b-9a77-1c2d3e4f5a6b
  • • Use a fresh key per distinct download; reuse it only when retrying that same download.
  • • Reusing a key with a different body returns IDEMPOTENCY_CONFLICT (409) rather than silently serving the wrong asset.
  • • Keep the key alongside your own record of the request so a retry after a crash can still find it.

Error codes

Every failure has the same shape: success: false, a requestId to quote in support requests, and an error.code from this table. Branch on the code, not the message.

CodeHTTPMeaning
UNAUTHORIZED401No credential was sent, or the JWT is expired or malformed.
INVALID_API_KEY401The X-API-Key value does not match any key.
API_KEY_REVOKED401The key existed but has been revoked. Create a new one.
USER_SUSPENDED403The account behind the credential is suspended or disabled.
INVALID_PLATFORM400platform must be exactly 'freepik' or 'envato-elements'.
INVALID_ASSET400Neither a usable url nor assetId was supplied, or the asset does not exist.
INVALID_ASSET_URL400The url host is not an allowed host for that platform, or the shape is wrong.
INSUFFICIENT_BALANCE402Your USD balance cannot cover this download. Top up and retry.
PROVIDER_CONFIGURATION_MISSING502No provider is configured for that platform on our side.
PROVIDER_CONFIGURATION_INVALID502A provider is configured but its credentials or settings are unusable.
PROVIDER_AUTH_FAILED502The upstream provider rejected our credentials.
PROVIDER_TIMEOUT504The upstream provider did not answer in time. Safe to retry.
PROVIDER_FAILED502A provider failed for another reason.
ALL_PROVIDERS_FAILED502Every provider we could try failed for this asset. You are not charged.
IDEMPOTENCY_CONFLICT409The same Idempotency-Key was reused with a different request body.
RATE_LIMITED429You exceeded a rate limit. Back off and retry.
INTERNAL_ERROR500Something failed on our side. Retry, then contact support with the requestId.

A failed request that never produced a downloadUrl is charged $0.

Rate limits

Three limits apply at once; the tightest one that matches wins. Exceeding any of them returns RATE_LIMITED (429).

Per API key

60

requests / minute

Per user

300

requests / minute

Per IP address

600

requests / minute

On a 429, back off before retrying — reuse the same Idempotency-Key so the retry stays safe.