[{"data":1,"prerenderedAt":423},["ShallowReactive",2],{"changelog:off-route":3,"\u002Ferrors":4,"docs-nav":391},null,{"id":5,"title":6,"body":7,"cards":3,"description":379,"extension":380,"group":381,"hero":3,"icon":382,"meta":383,"navTitle":3,"navigation":384,"order":385,"path":386,"seo":387,"sitemap":388,"stem":13,"__hash__":390},"docs\u002Ferrors.md","Errors",{"type":8,"value":9,"toc":373},"minimark",[10,14,19,23,30,63,69,110,128,140,157,161,283,289,293,316,330,333,337,369],[11,12,6],"h1",{"id":13},"errors",[15,16,18],"h2",{"id":17},"two-shapes-and-how-to-tell-them-apart","Two shapes, and how to tell them apart",[20,21,22],"p",{},"Errors come back in one of two shapes depending on how far into the system your\nrequest got. Handle both — a client that parses only one will throw on the other\nexactly when something is already going wrong.",[20,24,25,29],{},[26,27,28],"strong",{},"Rejected at the door"," — authentication and the API's own rate limiting:",[31,32,37],"pre",{"className":33,"code":34,"language":35,"meta":36,"style":36},"language-json shiki shiki-themes github-dark github-dark","{ \"message\": \"invalid API key\" }\n","json","",[38,39,40],"code",{"__ignoreMap":36},[41,42,45,49,53,56,60],"span",{"class":43,"line":44},"line",1,[41,46,48],{"class":47},"suv1-","{ ",[41,50,52],{"class":51},"s8ozJ","\"message\"",[41,54,55],{"class":47},": ",[41,57,59],{"class":58},"s4wv1","\"invalid API key\"",[41,61,62],{"class":47}," }\n",[20,64,65,68],{},[26,66,67],{},"Rejected by the endpoint"," — anything the API itself refused:",[31,70,72],{"className":33,"code":71,"language":35,"meta":36,"style":36},"{\n  \"code\": 3,\n  \"message\": \"limit must be at most 100, got 5000\"\n}\n",[38,73,74,79,93,104],{"__ignoreMap":36},[41,75,76],{"class":43,"line":44},[41,77,78],{"class":47},"{\n",[41,80,82,85,87,90],{"class":43,"line":81},2,[41,83,84],{"class":51},"  \"code\"",[41,86,55],{"class":47},[41,88,89],{"class":51},"3",[41,91,92],{"class":47},",\n",[41,94,96,99,101],{"class":43,"line":95},3,[41,97,98],{"class":51},"  \"message\"",[41,100,55],{"class":47},[41,102,103],{"class":58},"\"limit must be at most 100, got 5000\"\n",[41,105,107],{"class":43,"line":106},4,[41,108,109],{"class":47},"}\n",[20,111,112,113,120,121,123,124,127],{},"The safe way to read both: ",[26,114,115,116,119],{},"use the HTTP status for control flow, and ",[38,117,118],{},"message","\nfor the human."," Both shapes always carry ",[38,122,118],{},", so logging\n",[38,125,126],{},"response.message"," works either way.",[20,129,130,131,133,134,139],{},"One case carries neither shape: a request stopped by the per-IP edge ceiling never\nreaches the API, and comes back as the gateway's HTML error page. That is the one\nplace a ",[38,132,118],{}," lookup throws — see ",[135,136,138],"a",{"href":137},"#what-you-will-not-get","what you will not get",".",[20,141,142,143,145,146,149,150,152,153,156],{},"The ",[38,144,38],{}," in the second shape is ",[26,147,148],{},"not"," the HTTP status. It is a gRPC status\ncode, because these endpoints are generated from service definitions — ",[38,151,89],{}," is\ninvalid argument, ",[38,154,155],{},"5"," is not found. If you are branching on anything, branch on\nthe HTTP status.",[15,158,160],{"id":159},"status-codes","Status codes",[162,163,164,180],"table",{},[165,166,167],"thead",{},[168,169,170,174,177],"tr",{},[171,172,173],"th",{},"Status",[171,175,176],{},"Meaning",[171,178,179],{},"Worth retrying?",[181,182,183,206,223,236,257,270],"tbody",{},[168,184,185,191,203],{},[186,187,188],"td",{},[38,189,190],{},"400",[186,192,193,194,197,198,202],{},"Your request was malformed — a bad ",[38,195,196],{},"limit",", both cursors at once, an unknown enum value (",[135,199,201],{"href":200},"\u002Fglossary","glossary",")",[186,204,205],{},"No. Fix the request.",[168,207,208,213,216],{},[186,209,210],{},[38,211,212],{},"401",[186,214,215],{},"Missing, malformed or revoked token",[186,217,218,219,139],{},"No. See ",[135,220,222],{"href":221},"\u002Fauthentication","authentication",[168,224,225,230,233],{},[186,226,227],{},[38,228,229],{},"404",[186,231,232],{},"No such record, or not one you can see",[186,234,235],{},"No.",[168,237,238,243,250],{},[186,239,240],{},[38,241,242],{},"429",[186,244,245,249],{},[135,246,248],{"href":247},"\u002Frate-limits","Rate limit"," exceeded — your token's quota, or the per-IP edge ceiling",[186,251,252,253,256],{},"Yes. After ",[38,254,255],{},"Retry-After"," if the response carries one, otherwise back off.",[168,258,259,264,267],{},[186,260,261],{},[38,262,263],{},"500",[186,265,266],{},"Something broke on our side",[186,268,269],{},"Yes, with backoff. Report it if it persists.",[168,271,272,277,280],{},[186,273,274],{},[38,275,276],{},"503",[186,278,279],{},"No healthy backend behind the edge",[186,281,282],{},"Yes, with backoff.",[20,284,285,286,288],{},"A ",[38,287,229],{}," covers both \"this does not exist\" and \"this exists but is not yours\". That\nis intentional: distinguishing them would let anyone confirm whether a reference\nbelongs to another company.",[15,290,292],{"id":291},"retrying","Retrying",[20,294,295,296,298,299,301,302,304,305,298,307,309,310,312,313,315],{},"Retry ",[38,297,242],{},", ",[38,300,263],{}," and ",[38,303,276],{},". Do not retry ",[38,306,190],{},[38,308,212],{}," or ",[38,311,229],{}," — the same\nrequest will fail the same way, and a loop around a ",[38,314,212],{}," is how a token ends up\nlocked out of a log file rather than fixed.",[20,317,318,319,321,322,324,325,301,327,329],{},"For ",[38,320,242],{},", wait the ",[38,323,255],{}," seconds when the response carries one. The\nedge ceiling does not send the API's headers, so back off exponentially there\ninstead. For ",[38,326,263],{},[38,328,276],{},", do the same and give up after a handful of attempts\nrather than hammering a service that is already unwell.",[20,331,332],{},"Every read on this API is safe to repeat: nothing here changes state, so a retry\ncan never double-apply anything.",[15,334,336],{"id":335},"what-you-will-not-get","What you will not get",[338,339,340,354,363],"ul",{},[341,342,343,346,347,349,350,353],"li",{},[26,344,345],{},"No HTML from the API."," Everything the API itself refuses is JSON. The\nexception is the per-IP edge ceiling: it is enforced in front of the API and\nserves the gateway's own HTML error page, so do not assume a ",[38,348,242],{}," body is\nparseable — check the ",[38,351,352],{},"RateLimit-*"," headers instead.",[341,355,356,359,360,362],{},[26,357,358],{},"No stack traces or internal detail."," A ",[38,361,263],{}," tells you it failed, not what\nour database was doing. If you need to know, quote the time and the endpoint in\na support request.",[341,364,365,368],{},[26,366,367],{},"No partial pages."," A list request either returns a page or an error. It will\nnot return half the records and an error field.",[370,371,372],"style",{},"html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":36,"searchDepth":81,"depth":81,"links":374},[375,376,377,378],{"id":17,"depth":81,"text":18},{"id":159,"depth":81,"text":160},{"id":291,"depth":81,"text":292},{"id":335,"depth":81,"text":336},"Status codes, the two response shapes to expect, and which failures are worth retrying.","md","working","mdi-alert-outline",{},true,6,"\u002Ferrors",{"title":6,"description":379},{"loc":386,"lastmod":389},"2026-09-12","vmkI5phZp_6QYORupt7Rz5qF7-gMN9kXTCRAHLkEVKU",[392,397,400,404,407,412,413,417],{"path":393,"title":394,"navTitle":394,"icon":395,"group":396,"order":44},"\u002F","Overview","mdi-book-open-page-variant-outline","getting-started",{"path":221,"title":398,"navTitle":3,"icon":399,"group":396,"order":81},"Authentication","mdi-key-variant",{"path":401,"title":402,"navTitle":3,"icon":403,"group":381,"order":95},"\u002Ftracking","Shipment tracking","mdi-map-marker-path",{"path":247,"title":405,"navTitle":3,"icon":406,"group":381,"order":106},"Rate limits","mdi-speedometer",{"path":408,"title":409,"navTitle":3,"icon":410,"group":381,"order":411},"\u002Fpagination","Pagination and filtering","mdi-filter-variant",5,{"path":386,"title":6,"navTitle":3,"icon":382,"group":381,"order":385},{"path":200,"title":414,"navTitle":3,"icon":415,"group":381,"order":416},"Glossary","mdi-book-alphabet",7,{"path":418,"title":419,"navTitle":3,"icon":420,"group":421,"order":422},"\u002Fversioning","Versioning","mdi-tag-outline","current",8,1789636124841]