API credits and billing
Understand platform credit rates, confirmed content-restriction charges, free error cases, balance headers, and cache billing.
Successful parses are charged by platform. Most other supported platforms: 1 credit; YouTube: 3 credits; Threads: 2 credits; Kuaishou / Kwai: 2 credits; RedNote / Xiaohongshu: 10 credits. A failed request, empty response, or unavailable post alone does not trigger a charge. A no-media request is charged only when both a processing cost and a clear content restriction are confirmed. This exception currently applies to Douyin and Toutiao only. Unsupported URLs, input validation failures, invalid or unauthorized API tokens, temporary failures, timeouts, and unexplained empty results cost 0 credits. Unavailable content without a confirmed processing cost is also free.
| Platform | Credits per charged request |
|---|---|
| Most other supported platforms | 1 credit |
| YouTube | 3 credits |
| Threads | 2 credits |
| Kuaishou / Kwai | 2 credits |
| RedNote / Xiaohongshu | 10 credits |
Source of truth: GET /api/v1/capabilities → data.billing.
When requests use credits
| Request outcome | Credits charged |
|---|---|
| Downloadable media returned, including a cache hit | Platform rate, once |
| Confirmed processing cost AND confirmed content restriction (Douyin / Toutiao) | Platform rate, once |
| Invalid, expired, revoked, or platform-unauthorized API token | 0 |
| Unsupported URL or input validation failure | 0 |
| Temporary failure, timeout, or unexplained empty result | 0 |
| Unavailable content without a confirmed processing cost | 0 |
API token permission errors are not content restrictions and are free. Charging for processing does not mean EasyDown can retrieve private, deleted, or otherwise restricted content.
Each request is charged once at the platform rate, regardless of the number of media files, qualities, or internal processing attempts. Cache hits that return media use the same rate. A new submission is a new request, not a free retry.
Response
Charged commercial responses include the actual charge and balance, even when content is unavailable:
X-Credits-Charged: {actual_credits_charged}X-Credits-Remaining: {balance}X-EasyDown-Cache: HIT | MISS | SUPABASE | BYPASS
X-Credits-Charged reports the actual charge, including charged failures; X-Credits-Remaining reports the returned balance. Charged content-unavailable responses include X-Credits-Charge-Reason: content_unavailable.
REST and MCP use the same platform rates and confirmed content-restriction exception. MCP returns the actual charge in billing.creditsCharged, including on a failed result.
Do not infer a zero charge from an HTTP error, data: null, or the content_unavailable code. If a billing header is missing or the connection is interrupted, the charge is unknown: check dashboard usage records before resubmitting.
Billing question? Email [email protected] with the platform, request time, error code and usage-record details. Never send an API token. Historical charges are checked against their original records; this clarification does not change past transactions.
Illustrative failure responses
These are response-format illustrations, not newly captured paid requests. They show the platform-specific endpoint /api/v1/platforms/douyin/parse; balances are illustrative. Common endpoint status fields can differ.
Content unavailable after a confirmed processing cost: 1 credit charged
HTTP/1.1 404
Content-Type: application/json
X-Credits-Charged: 1
X-Credits-Remaining: 9
X-Credits-Charge-Reason: content_unavailable
{
"status": 404,
"data": null,
"msg": "Content unavailable after processing",
"code": "content_unavailable"
}Insufficient credits: 0 credits charged
HTTP/1.1 402
Content-Type: application/json
X-Credits-Charged: 0
X-Credits-Remaining: 0
{
"status": 402,
"data": null,
"msg": "Insufficient credits",
"code": "insufficient_credits"
}Check billing before handling an error
Read the billing headers before handling an error. Do not blindly retry a charged content-restriction failure; another submission can incur another charge.
const response = await fetch('https://api.easydown.org/api/v1/platforms/douyin/parse', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.EASYDOWN_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: "https://v.douyin.com/f-QpBB_oD9Y/" }),
});
const chargedHeader = response.headers.get('X-Credits-Charged');
const creditsCharged = chargedHeader === null ? null : Number(chargedHeader);
const chargeReason = response.headers.get('X-Credits-Charge-Reason');
const payload = await response.json();
console.log({ httpStatus: response.status, creditsCharged, chargeReason });
if (!response.ok || payload.status !== 200) {
if (creditsCharged === null || !Number.isFinite(creditsCharged)) {
console.warn('Charge unknown: check dashboard usage before resubmitting.');
}
// No automatic retry: another submission is a new request.
throw new Error(payload.code || 'parse_failed');
}