EN

Theme

Accent colour

Your theme and colour carry over to satelit.md and the app.

Dashboard Choose a plan

Credits and limits

Two separate limits: credits per month, which are your quota, and calls per minute, which protect the service.

Credits

  • A small response from the database costs 1 credit. The cost of each route is in the pricing table.
  • Credits are deducted only on a 2xx response. An error costs nothing.
  • They reset on the first day of your period, once a month. What you did not use does not carry over.
  • Over the quota you get 429 quota_exceeded. The API stops and no invoice appears.

The per-minute limit

Each plan has a per-minute limit. Over it you get 429 rate_limited, with the Retry-After header.

Limits by plan

Credits per monthCalls per minuteHistory
Start5,0003030 days
Creator30,00012090 days
Pro300,000600365 days
Business3,000,0001,800all
Enterpriseby contractby contractall

The headers of every response

HeaderWhat it says
X-RateLimit-LimitCalls allowed per minute
X-RateLimit-RemainingCalls left in the current minute
X-RateLimit-ResetSeconds until the minute resets
X-Credits-CostHow many credits this call cost
X-Credits-LimitThe period's credits
X-Credits-RemainingCredits remaining
X-Credits-ResetWhen credits reset (ISO 8601)
Retry-AfterHow many seconds to wait, only on 429 and 503

History

If you ask for more days than your plan allows, the response is cut at the plan's limit and meta.history_cut: true tells you so.

How to retry a call

Read Retry-After, wait that long and try again. On quota_exceeded do not retry: wait for the reset in X-Credits-Reset.

async function call(url, key) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
    const body = await res.json();
    if (res.ok) return body;
    if (body.error.code === "quota_exceeded") throw new Error(body.error.message);
    const wait = Number(res.headers.get("retry-after"));
    if (![429, 503].includes(res.status) || !wait) throw new Error(body.error.message);
    await new Promise((ok) => setTimeout(ok, wait * 1000));
  }
  throw new Error("still busy");
}
import json, time, urllib.error, urllib.request

def call(url, key):
    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {key}"})
    for attempt in range(3):
        try:
            with urllib.request.urlopen(req) as res:
                return json.load(res)
        except urllib.error.HTTPError as err:
            body = json.load(err)
            wait = err.headers.get("Retry-After")
            if err.code not in (429, 503) or not wait or body["error"]["code"] == "quota_exceeded":
                raise
            time.sleep(int(wait))
    raise RuntimeError("still busy")
<?php
function call(string $url, string $key): array {
    for ($attempt = 0; $attempt < 3; $attempt++) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $key"]]);
        $raw = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        $size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        curl_close($ch);
        $body = json_decode(substr($raw, $size), true);
        if ($status < 400) {
            return $body;
        }
        preg_match("/retry-after: (\\d+)/i", substr($raw, 0, $size), $m);
        if (!in_array($status, [429, 503]) || empty($m[1]) || $body["error"]["code"] === "quota_exceeded") {
            throw new RuntimeException($body["error"]["message"]);
        }
        sleep((int) $m[1]);
    }
    throw new RuntimeException("still busy");
}