Getting Started

Rate Limits, Pagination & Idempotency

TokPortal API rate limits (120 req/min), pagination with cursor-based offsets, and idempotency best practices.

Rate Limits & Pagination

Rate Limits

The TokPortal API allows 120 requests per minute per API key. The limit uses a token bucket algorithm that refills continuously, so you do not need to wait for a fixed window to reset.

Bundle capacity is a separate workspace-wide safeguard. Bundle creation and publication use rolling capacity windows shared by the workspace owner and every Team member. This capacity is not exposed as a live counter. A request is affected only when it would exceed the available capacity; it then returns HTTP 429 with capacity_cooldown and should be retried later.

Performance Max and API bulk creation are evaluated for the complete batch before credits are debited. If the batch would exceed capacity, the entire request is rejected rather than partially created. Do not split a rejected batch into parallel requests to work around the cooldown.

Authenticated responses include rate limit headers:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1779724800

X-RateLimit-Reset is a Unix timestamp estimating when the token bucket will be full again.

Rate Limit Response

When you exceed the limit, the API returns a 429 Too Many Requests response:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1779724800
Retry-After: 1
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please slow down.",
    "details": {
      "retry_after_seconds": 2
    }
  }
}

Best Practices

  • Read Retry-After first on 429 responses. It is the canonical wait time before retrying.
  • Check retry_after_seconds in the error response and wait before retrying.
  • Track X-RateLimit-Remaining to smooth bursts before you hit the limit.
  • Use exponential backoff — if you receive consecutive 429s, increase the wait time between retries.
  • Spread requests evenly rather than sending them in bursts. The token bucket refills at approximately 2 tokens per second, so a steady rate of 2 req/s is sustainable.
  • Use separate API keys for independent services to isolate rate limits.
  • Treat capacity_cooldown separately from API-key throttling. It is shared by the entire client workspace, so rotating keys or sending requests through a Team member does not create additional capacity.

Pagination

All list endpoints support pagination via query parameters.

Query Parameters

ParameterDefaultMaxDescription
page1The page number to retrieve (1-indexed).
per_page25100Number of items per page.

Example Request

curl -X GET "https://app.tokportal.com/api/ext/bundles?page=2&per_page=50" \
  -H "X-API-Key: sk_xxx"

Response Format

Paginated responses include a pagination object alongside data:

{
  "data": [
    { "id": "bnd_abc123", "status": "published" },
    { "id": "bnd_def456", "status": "configuring" }
  ],
  "pagination": {
    "page": 2,
    "per_page": 50,
    "total": 137,
    "total_pages": 3
  }
}

Iterating Through All Pages

async function fetchAllBundles(apiKey: string) {
  const results = [];
  let page = 1;

  while (true) {
    const res = await fetch(
      `https://app.tokportal.com/api/ext/bundles?page=${page}&per_page=100`,
      { headers: { "X-API-Key": apiKey } }
    );
    const { data, pagination } = await res.json();

    results.push(...data);

    if (page >= pagination.total_pages) break;
    page++;
  }

  return results;
}

Idempotency

For mutating requests (POST, PUT, PATCH, and DELETE) that document idempotency support, you can include an Idempotency-Key header to safely retry requests without creating duplicate resources or double-charging credits.

Do not send this header to an endpoint that can return a credential, verification code, webhook signing secret, signed upload URL, or private analytics report artifact. TokPortal rejects it before the operation runs with 400 IDEMPOTENCY_KEY_NOT_ALLOWED_FOR_SENSITIVE_RESPONSE and details.retry_without_idempotency_key: true. This prevents secret-bearing responses from being stored in the replay ledger. The affected operations are credential reveal, verification-code retrieval, webhook endpoint creation, presigned video or image upload creation, and analytics report creation/export. Follow the recovery instructions on the endpoint page instead.

Usage

curl -X POST https://app.tokportal.com/api/ext/bundles \
  -H "X-API-Key: sk_xxx" \
  -H "Idempotency-Key: my-unique-request-id-12345" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "tiktok", "country": "US", "videos": 10 }'

How It Works

  • The first request with a given Idempotency-Key is processed normally. After its response is durably stored as completed, exact retries return that response for 24 hours without executing the operation again.
  • Subsequent requests with the same key, method, path, and request body return the completed original response with Idempotent-Replayed: true.
  • Replayed responses include the Idempotent-Replayed: true header.
  • Reusing the same key with a different request body returns IDEMPOTENCY_KEY_REUSED.
  • Retrying while the first request is still running returns IDEMPOTENCY_KEY_IN_PROGRESS. Retry the exact request after a short delay.
  • If IDEMPOTENCY_KEY_IN_PROGRESS persists beyond the normal request duration, stop automated retries and do not switch to a new key. The mutation may have committed before TokPortal stored its response. The processing claim deliberately does not expire automatically because executing again could duplicate a resource or debit.
  • Persistent processing claims require TokPortal support/operator reconciliation against request, resource, and financial logs. Provide X-TokPortal-Request-ID, method, path, idempotency key, timestamp, and your non-secret correlation reference. Never send API keys, credentials, or verification codes.
  • Idempotency keys are scoped to your API key. Only completed responses expire after 24 hours from completion; unresolved processing claims remain fail-closed until reconciled.
  • Use a unique value per logical operation (e.g., a UUID or a meaningful identifier from your system).

When to Use Idempotency Keys

Idempotency keys are recommended for any operation that:

  • Creates a resource (bundles, video slots, edit slots)
  • Debits credits
  • Publishes, deletes, or updates state
  • Could be retried due to network timeouts or uncertain responses

This prevents accidental double-charges or duplicate resource creation.

Idempotency Errors

CodeHTTP StatusMeaning
IDEMPOTENCY_KEY_TOO_LONG400The key is longer than 255 characters.
IDEMPOTENCY_KEY_NOT_ALLOWED_FOR_SENSITIVE_RESPONSE400The endpoint can return sensitive material and rejected the key before execution. Remove the header and retry once.
IDEMPOTENCY_KEY_REUSED409The key already exists for a different request body.
IDEMPOTENCY_KEY_IN_PROGRESS409The claim is still processing. Retry the exact request briefly; if it persists, stop and request reconciliation. Never retry with a different key.
IDEMPOTENCY_UNAVAILABLE503The ledger could not be claimed, so no mutation ran. Retry the exact request with the same key.