Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.streampixel.io/llms.txt

Use this file to discover all available pages before exploring further.

Every Streampixel REST endpoint shares the same authentication model, error format, and rate-limit philosophy. This page is the single reference for what to expect when a request fails — and how to handle it correctly in production clients.

Rate limits

Streampixel applies per-user rate limits to write-heavy build pipeline endpoints to protect the build manager and downstream streaming infrastructure.
EndpointLimitScope
POST /projects/upload-file1 request per 2 minutesPer userId
POST /projects/distribute-file1 request per 2 minutesPer userId
All other endpointsUnlimited (subject to fair use)Per userId
“Unlimited” still means you must behave well. Aggressive polling, parallel fan-out, or repeated retries against any endpoint can be throttled or blocked at the edge. If you need high-frequency reads, contact support so we can scale your account appropriately.
When you exceed a limit, the API returns 429 Too Many Requests with a JSON body explaining when you can retry.
429 Too Many Requests
{
  "message": "Rate limit exceeded. Try again in 2 minutes."
}

Standard error response

Every error response — regardless of status code — uses the same JSON shape:
{
  "message": "Human-readable description of what went wrong"
}
There is no code, errors[], or nested envelope. Always parse message from the JSON body and surface it to operators or logs.
Successful responses (2xx) vary by endpoint and are documented on each endpoint’s page. The error shape above is uniform across the entire API.

HTTP status codes

StatusMeaningTypical example
200 OKRequest succeeded with a response body.GET /projects returns the list.
201 CreatedResource was created.New build accepted by the pipeline.
204 No ContentRequest succeeded; no body returned.Internal admin actions.
400 Bad RequestMalformed request — usually a missing or invalid field."Missing required field: fileUrl"
401 UnauthorizedapiKey is missing or invalid."Unauthorized: Invalid API Key"
403 ForbiddenThe authenticated user does not own the target resource."Forbidden: project does not belong to this user"
404 Not FoundThe resource does not exist."Project not found"
415 Unsupported Media TypeContent-Type is missing or not application/json."Content-Type must be application/json"
429 Too Many RequestsYou exceeded a rate limit window."Rate limit exceeded. Try again in 2 minutes."
500 Internal Server ErrorStreampixel had an unexpected failure."Internal server error"

Common causes

  • Required field omitted from the JSON body (e.g., fileUrl, projectId).
  • Wrong field type (number passed where a string was expected).
  • Empty string where a non-empty value is required.
  • Invalid fileUrl shape (must be a public direct download link).

Retry strategy

The rule of thumb:
  • Retry on 429 and 5xx.
  • Do not retry on any other 4xx — the request is wrong; retrying won’t fix it.
  • Use exponential backoff with jitter so a fleet of clients doesn’t synchronize on the same retry tick.

Pseudocode: backoff retry loop

Node.js
async function callWithRetry(fn, { maxAttempts = 5, baseDelayMs = 1000 } = {}) {
  let attempt = 0;

  while (true) {
    attempt++;
    try {
      return await fn();
    } catch (err) {
      const status = err.response?.status;

      // Permanent client errors — do not retry.
      if (status && status >= 400 && status < 500 && status !== 429) {
        throw err;
      }

      // Out of attempts.
      if (attempt >= maxAttempts) {
        throw err;
      }

      // Exponential backoff with jitter: 1s, 2s, 4s, 8s, 16s ± 250ms
      const delay = baseDelayMs * 2 ** (attempt - 1);
      const jitter = Math.floor(Math.random() * 500) - 250;
      await new Promise((r) => setTimeout(r, delay + jitter));
    }
  }
}
Python
import random
import time

def call_with_retry(fn, max_attempts=5, base_delay=1.0):
    attempt = 0
    while True:
        attempt += 1
        try:
            return fn()
        except Exception as err:
            status = getattr(err, "status_code", None) or getattr(err.response, "status_code", None)

            # Permanent client errors — do not retry.
            if status and 400 <= status < 500 and status != 429:
                raise

            if attempt >= max_attempts:
                raise

            delay = base_delay * (2 ** (attempt - 1))
            jitter = random.uniform(-0.25, 0.25)
            time.sleep(delay + jitter)
Cap your maximum delay (e.g., 60 seconds) and your maximum attempts (e.g., 5). Unbounded retries make incidents worse, not better.

Best practices

  • Always read message from the response body — never rely on the status code alone for human-friendly errors.
  • Log the full request and response (with apiKey redacted) when you hit an unexpected error. The body almost always pinpoints the issue.
  • Honor 429 — if you see one, slow down. Repeatedly hammering a rate-limited endpoint will get the entire user account temporarily blocked.
  • Treat 5xx as transient unless they persist beyond 2–3 retries spread over ~30 seconds. After that, alert and investigate.
  • Never retry idempotently destructive calls without confirming success first — for upload-file specifically, if the first call timed out, check the project’s build list before retrying.

Next steps

API authentication

How apiKey and userId work and where to obtain them.

Webhooks

Replace polling with push-based event delivery.