API

Guide 5 af 8

Fejl og rate limits

Alle fejl er JSON og har mindst et message. Nogle har mere.

{ "message": "Unauthenticated." }

Statuskoderne

Kode Betyder Hvad du gør
200 OK
401 Nøglen mangler, er ukendt, spærret eller udløbet Stop. Genforsøg hjælper ikke
403 Nøglen er gyldig, men mangler et scope Stop. Opret en nøgle med det rigtige scope
404 Ressourcen findes ikke i din organisation Stop. Se nedenfor
422 Ugyldige parametre Ret input
429 For mange kald Vent, og genforsøg efter Retry-After
5xx Fejl hos os Genforsøg med eksponentiel backoff

Kun 429 og 5xx er værd at genforsøge automatisk. Alt andet er en fejl i kaldet, og et blindt genforsøg gør det bare til den samme fejl igen, hurtigere.

403 fortæller hvilket scope der mangler

{
  "message": "Insufficient scope.",
  "required_scope": "invoices:read"
}

required_scope er der så din log kan sige "nøglen mangler invoices:read" i stedet for "adgang nægtet". Værdien er altid et navn fra scope-kataloget, aldrig fri tekst.

404 er også svaret på "findes i en anden organisation"

Det her overrasker de fleste, og det er med vilje.

Slår du et id op der findes, men hører til en anden organisation, får du 404, ikke 403. Der er intet i svaret der skelner de to tilfælde fra hinanden.

Grunden er at 403 ville være en oplysning. "Du må ikke se den her" bekræfter at rækken eksisterer, og med et endpoint der svarer forskelligt på "findes ikke" og "findes, men ikke hos dig" kan man afprøve id'er indtil man har kortlagt en anden organisations sager. Det hedder et enumeration-hul, og den eneste sikre rettelse er at de to tilfælde ser ens ud.

Så i din fejlhåndtering: 404 betyder "ikke her". Læs det ikke som "slettet", og skriv ikke en oprydning der sletter lokale rækker på et enkelt 404.

422 samler feltfejlene

{
  "message": "The given data was invalid.",
  "errors": {
    "per_page": ["The per page must be an integer."]
  }
}

errors er et objekt med et feltnavn per nøgle og en liste af beskeder. Feltnavnene er dem fra forespørgslen, så du kan sætte dem direkte på din egen formular.

Rate limits

Grænsen tælles pr. nøgle, ikke pr. IP. Standard er 120 kald i minuttet. Flere servere bag samme nøgle deler altså puljen, og to nøgler fra samme maskine gør ikke.

Hvert autentificeret svar bærer status med:

Header Indhold
X-RateLimit-Limit Kald tilladt i vinduet
X-RateLimit-Remaining Kald tilbage i det nuværende vindue
X-RateLimit-Reset Unix-tidsstempel for hvornår vinduet nulstilles

Rammer du loftet, får du 429 og en Retry-After i sekunder.

import time, requests

def call(url, headers, attempt=1):
    response = requests.get(url, headers=headers, timeout=30)

    if response.status_code == 429 and attempt <= 5:
        time.sleep(int(response.headers.get("Retry-After", 5)))
        return call(url, headers, attempt + 1)

    response.raise_for_status()
    return response.json()

Læs Retry-After frem for at gætte. Et fast sleep(1) i en løkke er den sikreste måde at holde sig selv i 429 resten af minuttet.

Vil du ikke ramme loftet i første omgang: hent større sider frem for flere. per_page=100 henter det samme som fire kald med per_page=25, for ét kald af budgettet.

Det uautentificerede endpoint /api/v1/openapi.json har sin egen, lavere grænse, fordi den ikke kan tælles pr. nøgle.

Hvad du ikke får i en fejl

Fejl indeholder ikke stack traces, interne id'er eller besked om hvilken organisation en ressource ellers ligger i. Kan du ikke se hvad der gik galt af message alene, så skriv til os med tidspunkt, endpoint og navnet på nøglen. Vi kan slå kaldet op i vores egne logs. Nøglens hemmelighed skal du ikke sende med.