[{"data":1,"prerenderedAt":2767},["ShallowReactive",2],{"api-nav":3,"api-guide:\u002Fapi\u002Frate-limits":351,"api-spec:guide:\u002Fapi\u002Frate-limits":2766},[4,28,57,95,115,301,317,331],{"id":5,"title":6,"links":7,"groups":27},"start","Start here",[8,11,15,18,21,24],{"title":9,"to":10},"Overview","\u002Fapi",{"title":12,"to":13,"status":14},"Quickstart","\u002Fapi\u002Fquickstart","available",{"title":16,"to":17,"status":14},"Environments","\u002Fapi\u002Fenvironments",{"title":19,"to":20,"status":14},"Authentication","\u002Fapi\u002Fauthentication",{"title":22,"to":23,"status":14},"Organizations","\u002Fapi\u002Forganizations",{"title":25,"to":26,"status":14},"Permissions","\u002Fapi\u002Fpermissions",[],{"id":29,"title":30,"links":31,"groups":56},"concepts","Concepts",[32,35,38,41,44,47,50,53],{"title":33,"to":34,"status":14},"Requests and responses","\u002Fapi\u002Frequests-and-responses",{"title":36,"to":37,"status":14},"Identifiers","\u002Fapi\u002Fidentifiers",{"title":39,"to":40,"status":14},"Pagination","\u002Fapi\u002Fpagination",{"title":42,"to":43},"Errors","\u002Fapi\u002Ferrors",{"title":45,"to":46,"status":14},"Rate limits","\u002Fapi\u002Frate-limits",{"title":48,"to":49,"status":14},"Retries","\u002Fapi\u002Fretries-and-idempotency",{"title":51,"to":52,"status":14},"Security","\u002Fapi\u002Fsecurity",{"title":54,"to":55,"status":14},"Versioning","\u002Fapi\u002Fversioning",[],{"id":58,"title":59,"links":60,"groups":94},"resources","Guides",[61,64,67,70,73,76,79,82,85,88,91],{"title":62,"to":63,"status":14},"Students","\u002Fapi\u002Fguides\u002Fstudents",{"title":65,"to":66,"status":14},"Bulk registration","\u002Fapi\u002Fguides\u002Fbulk-registration",{"title":68,"to":69,"status":14},"Passwords","\u002Fapi\u002Fguides\u002Fpasswords",{"title":71,"to":72,"status":14},"Supervisors","\u002Fapi\u002Fguides\u002Fsupervisors",{"title":74,"to":75,"status":14},"Reference data","\u002Fapi\u002Fguides\u002Freference-data",{"title":77,"to":78,"status":14},"API account","\u002Fapi\u002Fguides\u002Fapi-account",{"title":80,"to":81,"status":14},"Sign-in links","\u002Fapi\u002Fguides\u002Fsign-in-links",{"title":83,"to":84,"status":14},"Exams","\u002Fapi\u002Fguides\u002Fexams",{"title":86,"to":87,"status":14},"Applications","\u002Fapi\u002Fguides\u002Fapplications",{"title":89,"to":90,"status":14},"Group challenges","\u002Fapi\u002Fguides\u002Fgroup-challenges",{"title":92,"to":93,"status":14},"Certificates and reports","\u002Fapi\u002Fguides\u002Fcertificates-and-reports",[],{"id":96,"title":97,"links":98,"groups":114},"tutorials","Tutorials",[99,102,105,108,111],{"title":100,"to":101,"status":14},"Register and apply","\u002Fapi\u002Ftutorials\u002Fregister-and-apply",{"title":103,"to":104,"status":14},"Send a student to the panel","\u002Fapi\u002Ftutorials\u002Fsend-student-to-panel",{"title":106,"to":107,"status":14},"Change an application","\u002Fapi\u002Ftutorials\u002Fchange-an-application",{"title":109,"to":110,"status":14},"Collect results","\u002Fapi\u002Ftutorials\u002Fcollect-results",{"title":112,"to":113,"status":14},"Token handling","\u002Fapi\u002Ftutorials\u002Ftoken-handling",[],{"id":116,"title":117,"links":118,"groups":125},"reference","Reference",[119,122],{"title":120,"to":121},"All endpoints","\u002Fapi\u002Freference",{"title":123,"to":124},"Sandbox console","\u002Fapi\u002Fconsole",[126,135,145,166,206,212,230,255,271],{"tag":127,"slug":128,"links":129},"Health","health",[130],{"title":131,"to":132,"method":133,"deprecated":134},"Check that the API is up","\u002Fapi\u002Freference\u002Fget-health","GET",false,{"tag":77,"slug":136,"links":137},"api-account",[138,142],{"title":139,"to":140,"method":141,"deprecated":134},"Revoke the token you send, before it expires","\u002Fapi\u002Freference\u002Frevoke-token","POST",{"title":143,"to":144,"method":133,"deprecated":134},"Fetch the API account your token belongs to","\u002Fapi\u002Freference\u002Fget-current-api-account",{"tag":74,"slug":146,"links":147},"reference-data",[148,151,154,157,160,163],{"title":149,"to":150,"method":133,"deprecated":134},"List the countries a student can be registered in","\u002Fapi\u002Freference\u002Flist-countries",{"title":152,"to":153,"method":133,"deprecated":134},"Fetch one country by its id","\u002Fapi\u002Freference\u002Fget-country",{"title":155,"to":156,"method":133,"deprecated":134},"List the grades a student can be registered with","\u002Fapi\u002Freference\u002Flist-grades",{"title":158,"to":159,"method":133,"deprecated":134},"Fetch one grade by its id","\u002Fapi\u002Freference\u002Fget-grade",{"title":161,"to":162,"method":133,"deprecated":134},"List the organizations and their ids","\u002Fapi\u002Freference\u002Flist-organizations",{"title":164,"to":165,"method":133,"deprecated":134},"Fetch one organization by its id","\u002Fapi\u002Freference\u002Fget-organization",{"tag":62,"slug":167,"links":168},"students",[169,172,175,178,181,184,187,191,194,197,200,203],{"title":170,"to":171,"method":133,"deprecated":134},"List your students","\u002Fapi\u002Freference\u002Flist-students",{"title":173,"to":174,"method":141,"deprecated":134},"Register a student","\u002Fapi\u002Freference\u002Fregister-student",{"title":176,"to":177,"method":141,"deprecated":134},"Check a registration without registering the student","\u002Fapi\u002Freference\u002Fcheck-student-registration",{"title":179,"to":180,"method":141,"deprecated":134},"Register many students at once","\u002Fapi\u002Freference\u002Fcreate-student-import",{"title":182,"to":183,"method":133,"deprecated":134},"Follow a batch of students you sent","\u002Fapi\u002Freference\u002Fget-student-import",{"title":185,"to":186,"method":133,"deprecated":134},"Fetch one of your students","\u002Fapi\u002Freference\u002Fget-student",{"title":188,"to":189,"method":190,"deprecated":134},"Update one of your students","\u002Fapi\u002Freference\u002Fupdate-student","PUT",{"title":192,"to":193,"method":190,"deprecated":134},"Set the sign-in password of one of your students","\u002Fapi\u002Freference\u002Fset-student-password",{"title":195,"to":196,"method":133,"deprecated":134},"List your students who can use this organization","\u002Fapi\u002Freference\u002Flist-org-students",{"title":198,"to":199,"method":133,"deprecated":134},"Fetch one of your students, if they can use this organization","\u002Fapi\u002Freference\u002Fget-org-student",{"title":201,"to":202,"method":190,"deprecated":134},"Update one of your students and give them access to this organization","\u002Fapi\u002Freference\u002Fupdate-org-student",{"title":204,"to":205,"method":190,"deprecated":134},"Link one of your students to a supervisor on this organization","\u002Fapi\u002Freference\u002Flink-student-supervisor",{"tag":80,"slug":207,"links":208},"sign-in-links",[209],{"title":210,"to":211,"method":141,"deprecated":134},"Create a single-use sign-in link for one of your students","\u002Fapi\u002Freference\u002Fcreate-signin-link",{"tag":83,"slug":213,"links":214},"exams",[215,218,221,224,227],{"title":216,"to":217,"method":133,"deprecated":134},"List the exams open for applications","\u002Fapi\u002Freference\u002Flist-exams",{"title":219,"to":220,"method":133,"deprecated":134},"List an organization’s exam categories","\u002Fapi\u002Freference\u002Flist-exam-categories",{"title":222,"to":223,"method":133,"deprecated":134},"Fetch one exam category","\u002Fapi\u002Freference\u002Fget-exam-category",{"title":225,"to":226,"method":133,"deprecated":134},"List the exams one of your students can apply to","\u002Fapi\u002Freference\u002Flist-available-exams",{"title":228,"to":229,"method":133,"deprecated":134},"Fetch one exam that is open for applications","\u002Fapi\u002Freference\u002Fget-exam",{"tag":86,"slug":231,"links":232},"applications",[233,236,239,242,245,248,251],{"title":234,"to":235,"method":133,"deprecated":134},"List your students’ applications in this organization","\u002Fapi\u002Freference\u002Flist-applications",{"title":237,"to":238,"method":141,"deprecated":134},"Enter one of your students for an exam","\u002Fapi\u002Freference\u002Fcreate-application",{"title":240,"to":241,"method":133,"deprecated":134},"List your students’ applications for one exam","\u002Fapi\u002Freference\u002Flist-exam-applications",{"title":243,"to":244,"method":133,"deprecated":134},"List one of your students’ applications in this organization","\u002Fapi\u002Freference\u002Flist-student-applications",{"title":246,"to":247,"method":133,"deprecated":134},"Fetch one of your students’ applications","\u002Fapi\u002Freference\u002Fget-application",{"title":249,"to":250,"method":190,"deprecated":134},"Move one of your students’ applications to another exam","\u002Fapi\u002Freference\u002Fmove-application",{"title":252,"to":253,"method":254,"deprecated":134},"Withdraw one of your students from an exam","\u002Fapi\u002Freference\u002Fdelete-application","DELETE",{"tag":256,"slug":257,"links":258},"Documents","documents",[259,262,265,268],{"title":260,"to":261,"method":133,"deprecated":134},"Download a certificate file","\u002Fapi\u002Freference\u002Fdownload-certificate",{"title":263,"to":264,"method":133,"deprecated":134},"List one of your students’ released certificates","\u002Fapi\u002Freference\u002Flist-student-certificates",{"title":266,"to":267,"method":133,"deprecated":134},"Download a result report file","\u002Fapi\u002Freference\u002Fdownload-report",{"title":269,"to":270,"method":133,"deprecated":134},"List one of your students’ released result reports","\u002Fapi\u002Freference\u002Flist-student-reports",{"tag":89,"slug":272,"links":273},"group-challenges",[274,277,280,283,286,289,292,295,298],{"title":275,"to":276,"method":133,"deprecated":134},"List the group challenges an organization runs","\u002Fapi\u002Freference\u002Flist-group-challenges",{"title":278,"to":279,"method":133,"deprecated":134},"Fetch one group challenge","\u002Fapi\u002Freference\u002Fget-group-challenge",{"title":281,"to":282,"method":133,"deprecated":134},"List the groups your students are in for a group challenge","\u002Fapi\u002Freference\u002Flist-group-challenge-groups",{"title":284,"to":285,"method":133,"deprecated":134},"Fetch one group, with its steps and files","\u002Fapi\u002Freference\u002Fget-group-challenge-group",{"title":287,"to":288,"method":133,"deprecated":134},"List what has happened in one group","\u002Fapi\u002Freference\u002Flist-group-challenge-activity",{"title":290,"to":291,"method":141,"deprecated":134},"Send a group’s finished work for one of your students","\u002Fapi\u002Freference\u002Fsubmit-group-challenge-work",{"title":293,"to":294,"method":141,"deprecated":134},"Submit one step of a group for one of your students","\u002Fapi\u002Freference\u002Fsubmit-group-challenge-step",{"title":296,"to":297,"method":133,"deprecated":134},"List your students’ eligibility and groups for a group challenge","\u002Fapi\u002Freference\u002Flist-group-challenge-students",{"title":299,"to":300,"method":133,"deprecated":134},"Fetch one of your students’ eligibility and group for a group challenge","\u002Fapi\u002Freference\u002Fget-group-challenge-student",{"id":302,"title":303,"links":304,"groups":316},"clients","Clients",[305,307,310,313],{"title":9,"to":306,"status":14},"\u002Fapi\u002Fclients",{"title":308,"to":309,"status":14},"Node.js","\u002Fapi\u002Fclients\u002Fnode",{"title":311,"to":312,"status":14},"PHP","\u002Fapi\u002Fclients\u002Fphp",{"title":314,"to":315,"status":14},"Build your own","\u002Fapi\u002Fclients\u002Fbuild-your-own",[],{"id":318,"title":319,"links":320,"groups":330},"agents","AI agents",[321,324,327],{"title":322,"to":323},"AI connections","\u002Fapi\u002Fmcp",{"title":325,"to":326},"What it can do","\u002Fapi\u002Fmcp\u002Ftools",{"title":328,"to":329},"Agent skills","\u002Fapi\u002Fskills",[],{"id":332,"title":333,"links":334,"groups":350},"help","Help",[335,338,341,344,347],{"title":336,"to":337,"status":14},"Glossary","\u002Fapi\u002Fglossary",{"title":339,"to":340,"status":14},"FAQ","\u002Fapi\u002Ffaq",{"title":342,"to":343,"status":14},"Troubleshooting","\u002Fapi\u002Ftroubleshooting",{"title":345,"to":346,"status":14},"Support","\u002Fapi\u002Fsupport",{"title":348,"to":349},"Changelog","\u002Fapi\u002Fchangelog",[],{"id":352,"title":45,"body":353,"description":2759,"extension":2760,"meta":2761,"navTitle":45,"navigation":1387,"operations":2762,"order":926,"path":46,"section":29,"seo":2763,"status":14,"stem":2764,"__hash__":2765},"apiGuides\u002Fapi\u002Frate-limits.md",{"type":354,"value":355,"toc":2745},"minimark",[356,360,365,375,384,389,399,415,418,458,464,468,479,482,545,549,552,556,559,655,666,670,679,728,786,862,865,869,872,977,981,990,1015,1060,1140,1146,1163,1166,1169,1215,1227,1231,1300,1309,2016,2019,2564,2567,2571,2574,2667,2677,2683,2687,2698,2724,2730,2741],[357,358,359],"p",{},"The API limits how fast each account can call each operation. The limit is generous for normal integrations, but bulk jobs such as a nightly sync or a registration import will reach it. This page explains exactly how requests are counted, what the headers tell you, what happens when you go over, and how to write a client that never does.",[361,362,364],"h2",{"id":363},"the-limit","The limit",[366,367,369],"callout",{"type":368},"note",[357,370,371],{},[372,373,374],"strong",{},"100 requests per 60 seconds, per API account, per operation.",[357,376,377,378,383],{},"The limit is the same for every account, and the same in both environments: production and the ",[379,380,382],"a",{"href":381},"\u002Fapi\u002Fenvironments#sandbox","sandbox"," apply the numbers on this page.",[385,386,388],"h3",{"id":387},"one-operation-has-a-lower-limit-of-its-own","One operation has a lower limit of its own",[366,390,391],{"type":368},[357,392,393],{},[372,394,395,398],{},[379,396,397],{"href":180},"createStudentImport",": 10 requests per hour, per API account.",[357,400,401,402,406,407,410,411,414],{},"One request to it registers up to 1000 students, so the budget is counted in batches rather than in\nrequests. Everything else on this page — how the counter works, the headers, what a ",[403,404,405],"code",{},"429"," means —\nworks the same way for it; only the numbers differ. ",[403,408,409],{},"X-RateLimit-Limit"," on its answers reads ",[403,412,413],{},"10",".",[357,416,417],{},"Two further limits bound it, and neither is a rate limit:",[419,420,421,444],"ul",{},[422,423,424,427,428,431,432,434,435,439,440,443],"li",{},[372,425,426],{},"One unfinished import per account."," A second one while the first is running is ",[403,429,430],{},"409",", not\n",[403,433,405],{},". Sending the ",[436,437,438],"em",{},"same"," rows again is neither: it answers ",[403,441,442],{},"202"," with the import you already\nhave.",[422,445,446,449,450,453,454,457],{},[372,447,448],{},"One check per server at a time."," Checking a batch is the expensive part, so a server already\nchecking one answers ",[403,451,452],{},"503"," with ",[403,455,456],{},"Retry-After",". Nothing was queued.",[357,459,460,461,414],{},"See ",[379,462,65],{"href":463},"\u002Fapi\u002Fguides\u002Fbulk-registration#limits",[361,465,467],{"id":466},"what-per-operation-means","What \"per operation\" means",[357,469,470,471,474,475,478],{},"Each operation, such as ",[379,472,473],{"href":171},"listStudents"," or ",[379,476,477],{"href":238},"createApplication",", has its own counter for your account. Calling one operation does not use up another's budget, so different operations can run side by side at full speed.",[357,480,481],{},"The counter belongs to the operation, not to the URL:",[419,483,484,498,514],{},[422,485,486,489,490,493,494,497],{},[372,487,488],{},"Different ids share one counter."," ",[403,491,492],{},"GET \u002Fv1\u002F\u003CorganizationId>\u002Fapplication\u002F\u003CA>"," and ",[403,495,496],{},"GET \u002Fv1\u002F\u003CorganizationId>\u002Fapplication\u002F\u003CB>"," are the same operation.",[422,499,500,489,503,493,506,509,510,513],{},[372,501,502],{},"Different organizations share one counter.",[403,504,505],{},"GET \u002Fv1\u002F\u003CstemId>\u002Fexam",[403,507,508],{},"GET \u002Fv1\u002F\u003CneoId>\u002Fexam"," are the same operation, ",[379,511,512],{"href":217},"listExams",", so together they get 100 requests per 60 seconds, not 100 each.",[422,515,516,519,520,489,522,493,525,489,528,531,532,493,535,538,539,493,542,414],{},[372,517,518],{},"Two operations never share a counter",", even when they do similar work. ",[379,521,473],{"href":171},[403,523,524],{},"GET \u002Fv1\u002Fstudent",[379,526,527],{"href":196},"listOrgStudents",[403,529,530],{},"GET \u002Fv1\u002F\u003CorganizationId>\u002Fstudent"," are two operations with two counters. So are the two single student reads, ",[379,533,534],{"href":186},"getStudent",[379,536,537],{"href":199},"getOrgStudent",", and the two student updates, ",[379,540,541],{"href":189},"updateStudent",[379,543,544],{"href":202},"updateOrgStudent",[361,546,548],{"id":547},"counted-per-account-not-per-server","Counted per account, not per server",[357,550,551],{},"The count belongs to your API account, wherever the requests come from. Two servers, ten containers or a fleet of workers using the same account all draw on the same budget, so spreading calls over more machines does not raise the limit. Two different API accounts each have their own budget, even when they call from the same address.",[361,553,555],{"id":554},"what-counts","What counts",[357,557,558],{},"A request is counted once the API knows it is yours and allowed. That means: your token has been accepted, the organization in the path exists, and your account holds the permission.",[560,561,562,575],"table",{},[563,564,565],"thead",{},[566,567,568,572],"tr",{},[569,570,571],"th",{},"Counted",[569,573,574],{},"Not counted",[576,577,578,590,612,625,638,645],"tbody",{},[566,579,580,584],{},[581,582,583],"td",{},"Every successful request, file downloads included",[581,585,586,589],{},[403,587,588],{},"401 unauthorized",": the token was refused",[566,591,592,606],{},[581,593,594,595,598,599,602,603,605],{},"Requests the operation itself refuses: ",[403,596,597],{},"400"," for a body that breaks a rule, ",[403,600,601],{},"404"," for a record it cannot find, ",[403,604,430],{}," for a conflict",[581,607,608,611],{},[403,609,610],{},"403 forbidden"," for a missing permission",[566,613,614,616],{},[581,615],{},[581,617,618,621,622],{},[403,619,620],{},"404 not_found",", ",[403,623,624],{},"Organization not found!",[566,626,627,629],{},[581,628],{},[581,630,631,621,634,637],{},[403,632,633],{},"413",[403,635,636],{},"415"," and malformed JSON: the body was refused before anything else happened",[566,639,640,642],{},[581,641],{},[581,643,644],{},"A path that matches no operation",[566,646,647,649],{},[581,648],{},[581,650,651,654],{},[403,652,653],{},"GET \u002Fv1\u002Fhealth",", which no account's budget covers",[357,656,657,658,660,661,665],{},"The reason for the split: the counter needs to know which account a request belongs to, and that is only settled once the token, the organization and the permission have all been checked. Separately, the network in front of the API limits how fast each client address may send requests, whether or not they carry a token. That limit is not part of your account's budget, and a request it refuses gets a ",[403,659,405],{}," without the JSON error envelope (see ",[379,662,664],{"href":663},"\u002Fapi\u002Ferrors#too_many_requests","too_many_requests",").",[361,667,669],{"id":668},"the-headers","The headers",[357,671,672,673,675,676,678],{},"Every counted response carries three headers, including counted error responses such as a ",[403,674,601],{}," or a ",[403,677,430],{},":",[560,680,681,691],{},[563,682,683],{},[566,684,685,688],{},[569,686,687],{},"Header",[569,689,690],{},"Meaning",[576,692,693,705,718],{},[566,694,695,699],{},[581,696,697],{},[403,698,409],{},[581,700,701,702],{},"The limit for this operation: ",[403,703,704],{},"100",[566,706,707,712],{},[581,708,709],{},[403,710,711],{},"X-RateLimit-Remaining",[581,713,714,715],{},"How many requests you have left in the current window, never below ",[403,716,717],{},"0",[566,719,720,725],{},[581,721,722],{},[403,723,724],{},"X-RateLimit-Reset",[581,726,727],{},"Seconds until the current window ends, rounded up",[729,730,735],"pre",{"className":731,"code":732,"language":733,"meta":734,"style":734},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast","curl -sS -D - -o \u002Fdev\u002Fnull \"https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fstudent?limit=100\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n","bash","",[403,736,737,770],{"__ignoreMap":734},[738,739,742,746,750,753,757,760,763,766],"span",{"class":740,"line":741},"line",1,[738,743,745],{"class":744},"soyes","curl",[738,747,749],{"class":748},"s-5SL"," -sS",[738,751,752],{"class":748}," -D",[738,754,756],{"class":755},"sT6z2"," -",[738,758,759],{"class":748}," -o",[738,761,762],{"class":755}," \u002Fdev\u002Fnull",[738,764,765],{"class":755}," \"https:\u002F\u002Fapi.main-team.org\u002Fv1\u002Fstudent?limit=100\"",[738,767,769],{"class":768},"sHUrx"," \\\n",[738,771,773,776,779,783],{"class":740,"line":772},2,[738,774,775],{"class":748},"  -H",[738,777,778],{"class":755}," \"Authorization: Bearer ",[738,780,782],{"class":781},"suds8","$TOKEN",[738,784,785],{"class":755},"\"\n",[729,787,791],{"className":788,"code":789,"language":790,"meta":734,"style":734},"language-http shiki shiki-themes github-light-high-contrast github-dark-high-contrast","HTTP\u002F1.1 200 OK\nContent-Type: application\u002Fjson; charset=utf-8\nX-Request-Id: 4e3d2c1b-0a9f-4e8d-b7c6-a5b4c3d2e1f0\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 42\nX-RateLimit-Reset: 17\n","http",[403,792,793,810,821,832,842,852],{"__ignoreMap":734},[738,794,795,798,801,804,807],{"class":740,"line":741},[738,796,797],{"class":768},"HTTP",[738,799,800],{"class":781},"\u002F",[738,802,803],{"class":748},"1.1",[738,805,806],{"class":748}," 200",[738,808,809],{"class":755}," OK\n",[738,811,812,816,818],{"class":740,"line":772},[738,813,815],{"class":814},"sne4z","Content-Type",[738,817,678],{"class":768},[738,819,820],{"class":755}," application\u002Fjson; charset=utf-8\n",[738,822,824,827,829],{"class":740,"line":823},3,[738,825,826],{"class":814},"X-Request-Id",[738,828,678],{"class":768},[738,830,831],{"class":755}," 4e3d2c1b-0a9f-4e8d-b7c6-a5b4c3d2e1f0\n",[738,833,835,837,839],{"class":740,"line":834},4,[738,836,409],{"class":814},[738,838,678],{"class":768},[738,840,841],{"class":755}," 100\n",[738,843,845,847,849],{"class":740,"line":844},5,[738,846,711],{"class":814},[738,848,678],{"class":768},[738,850,851],{"class":755}," 42\n",[738,853,855,857,859],{"class":740,"line":854},6,[738,856,724],{"class":814},[738,858,678],{"class":768},[738,860,861],{"class":755}," 17\n",[357,863,864],{},"This response means: 42 more requests to this operation are allowed in the next 17 seconds. After that the counter starts again at 100.",[361,866,868],{"id":867},"how-the-window-works","How the window works",[357,870,871],{},"The window is fixed, not rolling. Your first counted request to an operation starts a 60-second window for that operation. Each counted request uses one of the 100. When the 60 seconds are up, the window ends, and your next request starts a fresh one.",[560,873,874,892],{},[563,875,876],{},[566,877,878,881,884,888],{},[569,879,880],{},"Time",[569,882,883],{},"Request",[569,885,886],{},[403,887,711],{},[569,889,890],{},[403,891,724],{},[576,893,894,914,932,949,961],{},[566,895,896,899,904,909],{},[581,897,898],{},"0:00",[581,900,901,902],{},"1st request to ",[403,903,473],{},[581,905,906],{},[403,907,908],{},"99",[581,910,911],{},[403,912,913],{},"60",[566,915,916,919,922,927],{},[581,917,918],{},"0:30",[581,920,921],{},"60th",[581,923,924],{},[403,925,926],{},"40",[581,928,929],{},[403,930,931],{},"30",[566,933,934,937,940,944],{},[581,935,936],{},"0:40",[581,938,939],{},"100th",[581,941,942],{},[403,943,717],{},[581,945,946],{},[403,947,948],{},"20",[566,950,951,954,957,959],{},[581,952,953],{},"1:00",[581,955,956],{},"The window ends",[581,958],{},[581,960],{},[566,962,963,966,969,973],{},[581,964,965],{},"1:05",[581,967,968],{},"Next request",[581,970,971],{},[403,972,908],{},[581,974,975],{},[403,976,913],{},[361,978,980],{"id":979},"going-over-a-429-and-a-60-second-pause","Going over: a 429 and a 60-second pause",[357,982,983,984,986,987,989],{},"If you send another request to an operation after ",[403,985,711],{}," has reached ",[403,988,717],{},", within the same window, it is refused:",[419,991,992,1000,1008],{},[422,993,994,995,997,998,414],{},"Status ",[403,996,405],{},", code ",[403,999,664],{},[422,1001,1002,1003,1005,1006,414],{},"A ",[403,1004,456],{}," header: the number of seconds to wait. The first refusal says ",[403,1007,913],{},[422,1009,1010,1011,1014],{},"No ",[403,1012,1013],{},"X-RateLimit-*"," headers.",[729,1016,1018],{"className":788,"code":1017,"language":790,"meta":734,"style":734},"HTTP\u002F1.1 429 Too Many Requests\nContent-Type: application\u002Fjson; charset=utf-8\nRetry-After: 60\nX-Request-Id: 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\n",[403,1019,1020,1034,1042,1051],{"__ignoreMap":734},[738,1021,1022,1024,1026,1028,1031],{"class":740,"line":741},[738,1023,797],{"class":768},[738,1025,800],{"class":781},[738,1027,803],{"class":748},[738,1029,1030],{"class":748}," 429",[738,1032,1033],{"class":755}," Too Many Requests\n",[738,1035,1036,1038,1040],{"class":740,"line":772},[738,1037,815],{"class":814},[738,1039,678],{"class":768},[738,1041,820],{"class":755},[738,1043,1044,1046,1048],{"class":740,"line":823},[738,1045,456],{"class":814},[738,1047,678],{"class":768},[738,1049,1050],{"class":755}," 60\n",[738,1052,1053,1055,1057],{"class":740,"line":834},[738,1054,826],{"class":814},[738,1056,678],{"class":768},[738,1058,1059],{"class":755}," 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\n",[729,1061,1065],{"className":1062,"code":1063,"language":1064,"meta":734,"style":734},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast","{\n  \"error\": {\n    \"code\": \"too_many_requests\",\n    \"message\": \"Too many requests to this operation. Wait the number of seconds in Retry-After, then try again.\",\n    \"documentation_url\": \"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#too_many_requests\",\n    \"request_id\": \"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\"\n  }\n}\n","json",[403,1066,1067,1072,1080,1094,1106,1118,1128,1134],{"__ignoreMap":734},[738,1068,1069],{"class":740,"line":741},[738,1070,1071],{"class":781},"{\n",[738,1073,1074,1077],{"class":740,"line":772},[738,1075,1076],{"class":814},"  \"error\"",[738,1078,1079],{"class":781},": {\n",[738,1081,1082,1085,1088,1091],{"class":740,"line":823},[738,1083,1084],{"class":814},"    \"code\"",[738,1086,1087],{"class":781},": ",[738,1089,1090],{"class":755},"\"too_many_requests\"",[738,1092,1093],{"class":781},",\n",[738,1095,1096,1099,1101,1104],{"class":740,"line":834},[738,1097,1098],{"class":814},"    \"message\"",[738,1100,1087],{"class":781},[738,1102,1103],{"class":755},"\"Too many requests to this operation. Wait the number of seconds in Retry-After, then try again.\"",[738,1105,1093],{"class":781},[738,1107,1108,1111,1113,1116],{"class":740,"line":844},[738,1109,1110],{"class":814},"    \"documentation_url\"",[738,1112,1087],{"class":781},[738,1114,1115],{"class":755},"\"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#too_many_requests\"",[738,1117,1093],{"class":781},[738,1119,1120,1123,1125],{"class":740,"line":854},[738,1121,1122],{"class":814},"    \"request_id\"",[738,1124,1087],{"class":781},[738,1126,1127],{"class":755},"\"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\"\n",[738,1129,1131],{"class":740,"line":1130},7,[738,1132,1133],{"class":781},"  }\n",[738,1135,1137],{"class":740,"line":1136},8,[738,1138,1139],{"class":781},"}\n",[357,1141,1142,1145],{},[372,1143,1144],{},"Going over costs you a full 60 seconds."," The first refused request starts a pause of 60 seconds for that operation on your account, even if the window would otherwise have ended sooner. During the pause:",[419,1147,1148,1157,1160],{},[422,1149,1150,1151,1153,1154,1156],{},"Every request to that operation is refused with ",[403,1152,405],{},", and its ",[403,1155,456],{}," counts down the seconds left.",[422,1158,1159],{},"Those refused requests are not counted and do not make the pause longer. They are wasted calls.",[422,1161,1162],{},"Every other operation keeps working normally.",[357,1164,1165],{},"When the pause ends, a fresh window of 100 begins.",[357,1167,1168],{},"Compare the two timelines:",[560,1170,1171,1184],{},[563,1172,1173],{},[566,1174,1175,1178,1181],{},[569,1176,1177],{},"Strategy",[569,1179,1180],{},"What happens",[569,1182,1183],{},"Time lost",[576,1185,1186,1204],{},[566,1187,1188,1198,1201],{},[581,1189,1190,1191,1194,1195,1197],{},"Stop at ",[403,1192,1193],{},"X-RateLimit-Remaining: 0"," (0:40) and wait ",[403,1196,724],{}," seconds",[581,1199,1200],{},"The window ends at 1:00, and the next request at 1:00 succeeds",[581,1202,1203],{},"20 seconds",[566,1205,1206,1209,1212],{},[581,1207,1208],{},"Send the 101st request at 0:45",[581,1210,1211],{},"Refused. Pause until 1:45; everything sent before then is refused",[581,1213,1214],{},"60 seconds",[366,1216,1218],{"type":1217},"warn",[357,1219,1220,1221,1223,1224,1226],{},"Do not retry a ",[403,1222,405],{}," in a tight loop. Every retry during the pause is refused, and none of them brings the end of the pause closer. Wait the ",[403,1225,456],{}," seconds, then send the request once.",[361,1228,1230],{"id":1229},"staying-under-the-limit","Staying under the limit",[1232,1233,1234,1248,1264,1280,1288,1294],"ol",{},[422,1235,1236,1241,1242,1244,1245,1247],{},[372,1237,1238,1239,414],{},"Watch ",[403,1240,711],{}," When it reaches ",[403,1243,717],{},", wait ",[403,1246,724],{}," seconds before your next call to that operation. You never pay the 60-second pause.",[422,1249,1250,1257,1258,1260,1261,414],{},[372,1251,1252,1253,1244,1255,1197],{},"On ",[403,1254,405],{},[403,1256,456],{},", plus a second or two of random jitter, then retry the same request. ",[403,1259,405],{}," is the only 4xx status worth retrying automatically; see ",[379,1262,1263],{"href":49},"Retries and idempotency",[422,1265,1266,1273,1274,1277,1278,414],{},[372,1267,1268,1269,1272],{},"Use ",[403,1270,1271],{},"limit=100"," on lists."," It is five times fewer requests than the default ",[403,1275,1276],{},"limit=20",". See ",[379,1279,39],{"href":40},[422,1281,1282,1285,1286,665],{},[372,1283,1284],{},"Cache what rarely changes."," Organizations, countries and grades are reference data; load them once and keep them (see ",[379,1287,74],{"href":75},[422,1289,1290,1293],{},[372,1291,1292],{},"Schedule bulk work."," Run imports and syncs as queued jobs that go at a steady pace, rather than firing everything at once.",[422,1295,1296,1299],{},[372,1297,1298],{},"Key your own budget by operation, not by URL."," One operation's counter covers every id and every organization in its path.",[357,1301,1302,1303,1306,1307,678],{},"Node.js (18 or later). A small per-operation gate: pass the operation name (for example ",[403,1304,1305],{},"'listStudents'","), and it waits when the budget for that operation is spent and retries after a ",[403,1308,405],{},[729,1310,1314],{"className":1311,"code":1312,"language":1313,"meta":734,"style":734},"language-js shiki shiki-themes github-light-high-contrast github-dark-high-contrast","const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\nconst budgets = new Map(); \u002F\u002F operation -> { remaining, resetAt }\n\n\u002F\u002F Use one name per operation, such as its operationId: every operation has its\n\u002F\u002F own counter, whichever ids and organization are in the path.\nexport async function withinLimit(operation, send, maxAttempts = 5) {\n  for (let attempt = 1; attempt \u003C= maxAttempts; attempt += 1) {\n    const budget = budgets.get(operation);\n    if (budget && budget.remaining \u003C= 0 && Date.now() \u003C budget.resetAt) {\n      await sleep(budget.resetAt - Date.now());\n    }\n\n    const res = await send(); \u002F\u002F must return a fetch Response\n\n    if (res.status === 429) {\n      const wait = Number(res.headers.get('retry-after') ?? 60);\n      budgets.set(operation, { remaining: 0, resetAt: Date.now() + wait * 1000 });\n      await sleep(wait * 1000 + Math.random() * 2000);\n      continue;\n    }\n\n    const remaining = res.headers.get('x-ratelimit-remaining');\n    const reset = res.headers.get('x-ratelimit-reset');\n    if (remaining !== null && reset !== null) {\n      budgets.set(operation, {\n        remaining: Number(remaining),\n        resetAt: Date.now() + Number(reset) * 1000,\n      });\n    }\n    return res;\n  }\n  throw new Error(`Still rate-limited after ${maxAttempts} attempts: ${operation}`);\n}\n\n\u002F\u002F Usage\nconst res = await withinLimit('application.create', () =>\n  fetch(`https:\u002F\u002Fapi.main-team.org\u002Fv1\u002F${organizationId}\u002Fapplication`, {\n    method: 'POST',\n    headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application\u002Fjson' },\n    body: JSON.stringify({ studentId, examId }),\n  }),\n);\n","js",[403,1315,1316,1362,1383,1389,1394,1399,1437,1472,1491,1529,1550,1556,1561,1582,1587,1602,1637,1673,1705,1714,1719,1724,1746,1767,1792,1802,1814,1837,1843,1848,1857,1862,1890,1895,1900,1906,1930,1950,1961,1988,2005,2011],{"__ignoreMap":734},[738,1317,1318,1321,1325,1328,1331,1334,1337,1340,1343,1346,1349,1352,1354,1356,1359],{"class":740,"line":741},[738,1319,1320],{"class":768},"const",[738,1322,1324],{"class":1323},"sKwhi"," sleep",[738,1326,1327],{"class":768}," =",[738,1329,1330],{"class":781}," (",[738,1332,1333],{"class":744},"ms",[738,1335,1336],{"class":781},") ",[738,1338,1339],{"class":768},"=>",[738,1341,1342],{"class":768}," new",[738,1344,1345],{"class":748}," Promise",[738,1347,1348],{"class":781},"((",[738,1350,1351],{"class":744},"resolve",[738,1353,1336],{"class":781},[738,1355,1339],{"class":768},[738,1357,1358],{"class":1323}," setTimeout",[738,1360,1361],{"class":781},"(resolve, ms));\n",[738,1363,1364,1366,1369,1371,1373,1376,1379],{"class":740,"line":772},[738,1365,1320],{"class":768},[738,1367,1368],{"class":748}," budgets",[738,1370,1327],{"class":768},[738,1372,1342],{"class":768},[738,1374,1375],{"class":1323}," Map",[738,1377,1378],{"class":781},"(); ",[738,1380,1382],{"class":1381},"sLBg1","\u002F\u002F operation -> { remaining, resetAt }\n",[738,1384,1385],{"class":740,"line":823},[738,1386,1388],{"emptyLinePlaceholder":1387},true,"\n",[738,1390,1391],{"class":740,"line":834},[738,1392,1393],{"class":1381},"\u002F\u002F Use one name per operation, such as its operationId: every operation has its\n",[738,1395,1396],{"class":740,"line":844},[738,1397,1398],{"class":1381},"\u002F\u002F own counter, whichever ids and organization are in the path.\n",[738,1400,1401,1404,1407,1410,1413,1416,1419,1421,1424,1426,1429,1431,1434],{"class":740,"line":854},[738,1402,1403],{"class":768},"export",[738,1405,1406],{"class":768}," async",[738,1408,1409],{"class":768}," function",[738,1411,1412],{"class":1323}," withinLimit",[738,1414,1415],{"class":781},"(",[738,1417,1418],{"class":744},"operation",[738,1420,621],{"class":781},[738,1422,1423],{"class":744},"send",[738,1425,621],{"class":781},[738,1427,1428],{"class":744},"maxAttempts",[738,1430,1327],{"class":768},[738,1432,1433],{"class":748}," 5",[738,1435,1436],{"class":781},") {\n",[738,1438,1439,1442,1444,1447,1450,1453,1456,1459,1462,1465,1468,1470],{"class":740,"line":1130},[738,1440,1441],{"class":768},"  for",[738,1443,1330],{"class":781},[738,1445,1446],{"class":768},"let",[738,1448,1449],{"class":781}," attempt ",[738,1451,1452],{"class":768},"=",[738,1454,1455],{"class":748}," 1",[738,1457,1458],{"class":781},"; attempt ",[738,1460,1461],{"class":768},"\u003C=",[738,1463,1464],{"class":781}," maxAttempts; attempt ",[738,1466,1467],{"class":768},"+=",[738,1469,1455],{"class":748},[738,1471,1436],{"class":781},[738,1473,1474,1477,1480,1482,1485,1488],{"class":740,"line":1136},[738,1475,1476],{"class":768},"    const",[738,1478,1479],{"class":748}," budget",[738,1481,1327],{"class":768},[738,1483,1484],{"class":781}," budgets.",[738,1486,1487],{"class":1323},"get",[738,1489,1490],{"class":781},"(operation);\n",[738,1492,1494,1497,1500,1503,1506,1508,1511,1514,1517,1520,1523,1526],{"class":740,"line":1493},9,[738,1495,1496],{"class":768},"    if",[738,1498,1499],{"class":781}," (budget ",[738,1501,1502],{"class":768},"&&",[738,1504,1505],{"class":781}," budget.remaining ",[738,1507,1461],{"class":768},[738,1509,1510],{"class":748}," 0",[738,1512,1513],{"class":768}," &&",[738,1515,1516],{"class":781}," Date.",[738,1518,1519],{"class":1323},"now",[738,1521,1522],{"class":781},"() ",[738,1524,1525],{"class":768},"\u003C",[738,1527,1528],{"class":781}," budget.resetAt) {\n",[738,1530,1532,1535,1537,1540,1543,1545,1547],{"class":740,"line":1531},10,[738,1533,1534],{"class":768},"      await",[738,1536,1324],{"class":1323},[738,1538,1539],{"class":781},"(budget.resetAt ",[738,1541,1542],{"class":768},"-",[738,1544,1516],{"class":781},[738,1546,1519],{"class":1323},[738,1548,1549],{"class":781},"());\n",[738,1551,1553],{"class":740,"line":1552},11,[738,1554,1555],{"class":781},"    }\n",[738,1557,1559],{"class":740,"line":1558},12,[738,1560,1388],{"emptyLinePlaceholder":1387},[738,1562,1564,1566,1569,1571,1574,1577,1579],{"class":740,"line":1563},13,[738,1565,1476],{"class":768},[738,1567,1568],{"class":748}," res",[738,1570,1327],{"class":768},[738,1572,1573],{"class":768}," await",[738,1575,1576],{"class":1323}," send",[738,1578,1378],{"class":781},[738,1580,1581],{"class":1381},"\u002F\u002F must return a fetch Response\n",[738,1583,1585],{"class":740,"line":1584},14,[738,1586,1388],{"emptyLinePlaceholder":1387},[738,1588,1590,1592,1595,1598,1600],{"class":740,"line":1589},15,[738,1591,1496],{"class":768},[738,1593,1594],{"class":781}," (res.status ",[738,1596,1597],{"class":768},"===",[738,1599,1030],{"class":748},[738,1601,1436],{"class":781},[738,1603,1605,1608,1611,1613,1616,1619,1621,1623,1626,1628,1631,1634],{"class":740,"line":1604},16,[738,1606,1607],{"class":768},"      const",[738,1609,1610],{"class":748}," wait",[738,1612,1327],{"class":768},[738,1614,1615],{"class":1323}," Number",[738,1617,1618],{"class":781},"(res.headers.",[738,1620,1487],{"class":1323},[738,1622,1415],{"class":781},[738,1624,1625],{"class":755},"'retry-after'",[738,1627,1336],{"class":781},[738,1629,1630],{"class":768},"??",[738,1632,1633],{"class":748}," 60",[738,1635,1636],{"class":781},");\n",[738,1638,1640,1643,1646,1649,1651,1654,1656,1658,1661,1664,1667,1670],{"class":740,"line":1639},17,[738,1641,1642],{"class":781},"      budgets.",[738,1644,1645],{"class":1323},"set",[738,1647,1648],{"class":781},"(operation, { remaining: ",[738,1650,717],{"class":748},[738,1652,1653],{"class":781},", resetAt: Date.",[738,1655,1519],{"class":1323},[738,1657,1522],{"class":781},[738,1659,1660],{"class":768},"+",[738,1662,1663],{"class":781}," wait ",[738,1665,1666],{"class":768},"*",[738,1668,1669],{"class":748}," 1000",[738,1671,1672],{"class":781}," });\n",[738,1674,1676,1678,1680,1683,1685,1687,1690,1693,1696,1698,1700,1703],{"class":740,"line":1675},18,[738,1677,1534],{"class":768},[738,1679,1324],{"class":1323},[738,1681,1682],{"class":781},"(wait ",[738,1684,1666],{"class":768},[738,1686,1669],{"class":748},[738,1688,1689],{"class":768}," +",[738,1691,1692],{"class":781}," Math.",[738,1694,1695],{"class":1323},"random",[738,1697,1522],{"class":781},[738,1699,1666],{"class":768},[738,1701,1702],{"class":748}," 2000",[738,1704,1636],{"class":781},[738,1706,1708,1711],{"class":740,"line":1707},19,[738,1709,1710],{"class":768},"      continue",[738,1712,1713],{"class":781},";\n",[738,1715,1717],{"class":740,"line":1716},20,[738,1718,1555],{"class":781},[738,1720,1722],{"class":740,"line":1721},21,[738,1723,1388],{"emptyLinePlaceholder":1387},[738,1725,1727,1729,1732,1734,1737,1739,1741,1744],{"class":740,"line":1726},22,[738,1728,1476],{"class":768},[738,1730,1731],{"class":748}," remaining",[738,1733,1327],{"class":768},[738,1735,1736],{"class":781}," res.headers.",[738,1738,1487],{"class":1323},[738,1740,1415],{"class":781},[738,1742,1743],{"class":755},"'x-ratelimit-remaining'",[738,1745,1636],{"class":781},[738,1747,1749,1751,1754,1756,1758,1760,1762,1765],{"class":740,"line":1748},23,[738,1750,1476],{"class":768},[738,1752,1753],{"class":748}," reset",[738,1755,1327],{"class":768},[738,1757,1736],{"class":781},[738,1759,1487],{"class":1323},[738,1761,1415],{"class":781},[738,1763,1764],{"class":755},"'x-ratelimit-reset'",[738,1766,1636],{"class":781},[738,1768,1770,1772,1775,1778,1781,1783,1786,1788,1790],{"class":740,"line":1769},24,[738,1771,1496],{"class":768},[738,1773,1774],{"class":781}," (remaining ",[738,1776,1777],{"class":768},"!==",[738,1779,1780],{"class":748}," null",[738,1782,1513],{"class":768},[738,1784,1785],{"class":781}," reset ",[738,1787,1777],{"class":768},[738,1789,1780],{"class":748},[738,1791,1436],{"class":781},[738,1793,1795,1797,1799],{"class":740,"line":1794},25,[738,1796,1642],{"class":781},[738,1798,1645],{"class":1323},[738,1800,1801],{"class":781},"(operation, {\n",[738,1803,1805,1808,1811],{"class":740,"line":1804},26,[738,1806,1807],{"class":781},"        remaining: ",[738,1809,1810],{"class":1323},"Number",[738,1812,1813],{"class":781},"(remaining),\n",[738,1815,1817,1820,1822,1824,1826,1828,1831,1833,1835],{"class":740,"line":1816},27,[738,1818,1819],{"class":781},"        resetAt: Date.",[738,1821,1519],{"class":1323},[738,1823,1522],{"class":781},[738,1825,1660],{"class":768},[738,1827,1615],{"class":1323},[738,1829,1830],{"class":781},"(reset) ",[738,1832,1666],{"class":768},[738,1834,1669],{"class":748},[738,1836,1093],{"class":781},[738,1838,1840],{"class":740,"line":1839},28,[738,1841,1842],{"class":781},"      });\n",[738,1844,1846],{"class":740,"line":1845},29,[738,1847,1555],{"class":781},[738,1849,1851,1854],{"class":740,"line":1850},30,[738,1852,1853],{"class":768},"    return",[738,1855,1856],{"class":781}," res;\n",[738,1858,1860],{"class":740,"line":1859},31,[738,1861,1133],{"class":781},[738,1863,1865,1868,1870,1873,1875,1878,1880,1883,1885,1888],{"class":740,"line":1864},32,[738,1866,1867],{"class":768},"  throw",[738,1869,1342],{"class":768},[738,1871,1872],{"class":1323}," Error",[738,1874,1415],{"class":781},[738,1876,1877],{"class":755},"`Still rate-limited after ${",[738,1879,1428],{"class":781},[738,1881,1882],{"class":755},"} attempts: ${",[738,1884,1418],{"class":781},[738,1886,1887],{"class":755},"}`",[738,1889,1636],{"class":781},[738,1891,1893],{"class":740,"line":1892},33,[738,1894,1139],{"class":781},[738,1896,1898],{"class":740,"line":1897},34,[738,1899,1388],{"emptyLinePlaceholder":1387},[738,1901,1903],{"class":740,"line":1902},35,[738,1904,1905],{"class":1381},"\u002F\u002F Usage\n",[738,1907,1909,1911,1913,1915,1917,1919,1921,1924,1927],{"class":740,"line":1908},36,[738,1910,1320],{"class":768},[738,1912,1568],{"class":748},[738,1914,1327],{"class":768},[738,1916,1573],{"class":768},[738,1918,1412],{"class":1323},[738,1920,1415],{"class":781},[738,1922,1923],{"class":755},"'application.create'",[738,1925,1926],{"class":781},", () ",[738,1928,1929],{"class":768},"=>\n",[738,1931,1933,1936,1938,1941,1944,1947],{"class":740,"line":1932},37,[738,1934,1935],{"class":1323},"  fetch",[738,1937,1415],{"class":781},[738,1939,1940],{"class":755},"`https:\u002F\u002Fapi.main-team.org\u002Fv1\u002F${",[738,1942,1943],{"class":781},"organizationId",[738,1945,1946],{"class":755},"}\u002Fapplication`",[738,1948,1949],{"class":781},", {\n",[738,1951,1953,1956,1959],{"class":740,"line":1952},38,[738,1954,1955],{"class":781},"    method: ",[738,1957,1958],{"class":755},"'POST'",[738,1960,1093],{"class":781},[738,1962,1964,1967,1970,1973,1975,1977,1980,1982,1985],{"class":740,"line":1963},39,[738,1965,1966],{"class":781},"    headers: { Authorization: ",[738,1968,1969],{"class":755},"`Bearer ${",[738,1971,1972],{"class":781},"token",[738,1974,1887],{"class":755},[738,1976,621],{"class":781},[738,1978,1979],{"class":755},"'Content-Type'",[738,1981,1087],{"class":781},[738,1983,1984],{"class":755},"'application\u002Fjson'",[738,1986,1987],{"class":781}," },\n",[738,1989,1991,1994,1997,1999,2002],{"class":740,"line":1990},40,[738,1992,1993],{"class":781},"    body: ",[738,1995,1996],{"class":748},"JSON",[738,1998,414],{"class":781},[738,2000,2001],{"class":1323},"stringify",[738,2003,2004],{"class":781},"({ studentId, examId }),\n",[738,2006,2008],{"class":740,"line":2007},41,[738,2009,2010],{"class":781},"  }),\n",[738,2012,2014],{"class":740,"line":2013},42,[738,2015,1636],{"class":781},[357,2017,2018],{},"PHP (8.1 or later). The same idea for a single long-running worker:",[729,2020,2024],{"className":2021,"code":2022,"language":2023,"meta":734,"style":734},"language-php shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003C?php\nfinal class RateGate\n{\n    \u002F** @var array\u003Cstring, array{remaining:int, resetAt:float}> *\u002F\n    private array $budgets = [];\n\n    \u002F**\n     * $send performs the request and returns [int $status, array $headers, string $body],\n     * with header names in lower case.\n     *\u002F\n    public function call(string $operation, callable $send, int $maxAttempts = 5): array\n    {\n        for ($attempt = 1; $attempt \u003C= $maxAttempts; $attempt++) {\n            $b = $this->budgets[$operation] ?? null;\n            if ($b !== null && $b['remaining'] \u003C= 0 && microtime(true) \u003C $b['resetAt']) {\n                usleep((int) (($b['resetAt'] - microtime(true)) * 1_000_000));\n            }\n\n            [$status, $headers, $body] = $send();\n\n            if ($status === 429) {\n                $wait = (int) ($headers['retry-after'] ?? 60);\n                $this->budgets[$operation] = ['remaining' => 0, 'resetAt' => microtime(true) + $wait];\n                usleep(($wait * 1_000_000) + random_int(0, 2_000_000));\n                continue;\n            }\n            if (isset($headers['x-ratelimit-remaining'], $headers['x-ratelimit-reset'])) {\n                $this->budgets[$operation] = [\n                    'remaining' => (int) $headers['x-ratelimit-remaining'],\n                    'resetAt' => microtime(true) + (int) $headers['x-ratelimit-reset'],\n                ];\n            }\n            return [$status, $headers, $body];\n        }\n        throw new RuntimeException(\"Still rate-limited after {$maxAttempts} attempts: {$operation}\");\n    }\n}\n","php",[403,2025,2026,2034,2045,2049,2068,2083,2087,2092,2097,2102,2107,2148,2153,2178,2200,2249,2284,2289,2293,2303,2307,2320,2344,2384,2413,2420,2424,2446,2459,2478,2505,2510,2514,2522,2527,2556,2560],{"__ignoreMap":734},[738,2027,2028,2031],{"class":740,"line":741},[738,2029,2030],{"class":768},"\u003C?",[738,2032,2033],{"class":748},"php\n",[738,2035,2036,2039,2042],{"class":740,"line":772},[738,2037,2038],{"class":768},"final",[738,2040,2041],{"class":768}," class",[738,2043,2044],{"class":744}," RateGate\n",[738,2046,2047],{"class":740,"line":823},[738,2048,1071],{"class":781},[738,2050,2051,2054,2057,2060,2062,2065],{"class":740,"line":834},[738,2052,2053],{"class":1381},"    \u002F** ",[738,2055,2056],{"class":768},"@var",[738,2058,2059],{"class":768}," array",[738,2061,1525],{"class":1381},[738,2063,2064],{"class":768},"string",[738,2066,2067],{"class":1381},", array{remaining:int, resetAt:float}> *\u002F\n",[738,2069,2070,2073,2075,2078,2080],{"class":740,"line":844},[738,2071,2072],{"class":768},"    private",[738,2074,2059],{"class":768},[738,2076,2077],{"class":781}," $budgets ",[738,2079,1452],{"class":768},[738,2081,2082],{"class":781}," [];\n",[738,2084,2085],{"class":740,"line":854},[738,2086,1388],{"emptyLinePlaceholder":1387},[738,2088,2089],{"class":740,"line":1130},[738,2090,2091],{"class":1381},"    \u002F**\n",[738,2093,2094],{"class":740,"line":1136},[738,2095,2096],{"class":1381},"     * $send performs the request and returns [int $status, array $headers, string $body],\n",[738,2098,2099],{"class":740,"line":1493},[738,2100,2101],{"class":1381},"     * with header names in lower case.\n",[738,2103,2104],{"class":740,"line":1531},[738,2105,2106],{"class":1381},"     *\u002F\n",[738,2108,2109,2112,2114,2117,2119,2121,2124,2127,2130,2133,2136,2138,2140,2143,2145],{"class":740,"line":1552},[738,2110,2111],{"class":768},"    public",[738,2113,1409],{"class":768},[738,2115,2116],{"class":1323}," call",[738,2118,1415],{"class":781},[738,2120,2064],{"class":768},[738,2122,2123],{"class":781}," $operation, ",[738,2125,2126],{"class":768},"callable",[738,2128,2129],{"class":781}," $send, ",[738,2131,2132],{"class":768},"int",[738,2134,2135],{"class":781}," $maxAttempts ",[738,2137,1452],{"class":768},[738,2139,1433],{"class":748},[738,2141,2142],{"class":781},")",[738,2144,678],{"class":768},[738,2146,2147],{"class":768}," array\n",[738,2149,2150],{"class":740,"line":1558},[738,2151,2152],{"class":781},"    {\n",[738,2154,2155,2158,2161,2163,2165,2168,2170,2173,2176],{"class":740,"line":1563},[738,2156,2157],{"class":768},"        for",[738,2159,2160],{"class":781}," ($attempt ",[738,2162,1452],{"class":768},[738,2164,1455],{"class":748},[738,2166,2167],{"class":781},"; $attempt ",[738,2169,1461],{"class":768},[738,2171,2172],{"class":781}," $maxAttempts; $attempt",[738,2174,2175],{"class":768},"++",[738,2177,1436],{"class":781},[738,2179,2180,2183,2185,2188,2191,2194,2196,2198],{"class":740,"line":1584},[738,2181,2182],{"class":781},"            $b ",[738,2184,1452],{"class":768},[738,2186,2187],{"class":748}," $this",[738,2189,2190],{"class":768},"->",[738,2192,2193],{"class":781},"budgets[$operation] ",[738,2195,1630],{"class":768},[738,2197,1780],{"class":748},[738,2199,1713],{"class":781},[738,2201,2202,2205,2208,2210,2212,2214,2217,2220,2223,2225,2227,2229,2232,2234,2237,2239,2241,2243,2246],{"class":740,"line":1589},[738,2203,2204],{"class":768},"            if",[738,2206,2207],{"class":781}," ($b ",[738,2209,1777],{"class":768},[738,2211,1780],{"class":748},[738,2213,1513],{"class":768},[738,2215,2216],{"class":781}," $b[",[738,2218,2219],{"class":755},"'remaining'",[738,2221,2222],{"class":781},"] ",[738,2224,1461],{"class":768},[738,2226,1510],{"class":748},[738,2228,1513],{"class":768},[738,2230,2231],{"class":748}," microtime",[738,2233,1415],{"class":781},[738,2235,2236],{"class":748},"true",[738,2238,1336],{"class":781},[738,2240,1525],{"class":768},[738,2242,2216],{"class":781},[738,2244,2245],{"class":755},"'resetAt'",[738,2247,2248],{"class":781},"]) {\n",[738,2250,2251,2254,2256,2258,2261,2263,2265,2267,2269,2271,2273,2276,2278,2281],{"class":740,"line":1604},[738,2252,2253],{"class":748},"                usleep",[738,2255,1348],{"class":781},[738,2257,2132],{"class":768},[738,2259,2260],{"class":781},") (($b[",[738,2262,2245],{"class":755},[738,2264,2222],{"class":781},[738,2266,1542],{"class":768},[738,2268,2231],{"class":748},[738,2270,1415],{"class":781},[738,2272,2236],{"class":748},[738,2274,2275],{"class":781},")) ",[738,2277,1666],{"class":768},[738,2279,2280],{"class":748}," 1_000_000",[738,2282,2283],{"class":781},"));\n",[738,2285,2286],{"class":740,"line":1639},[738,2287,2288],{"class":781},"            }\n",[738,2290,2291],{"class":740,"line":1675},[738,2292,1388],{"emptyLinePlaceholder":1387},[738,2294,2295,2298,2300],{"class":740,"line":1707},[738,2296,2297],{"class":781},"            [$status, $headers, $body] ",[738,2299,1452],{"class":768},[738,2301,2302],{"class":781}," $send();\n",[738,2304,2305],{"class":740,"line":1716},[738,2306,1388],{"emptyLinePlaceholder":1387},[738,2308,2309,2311,2314,2316,2318],{"class":740,"line":1721},[738,2310,2204],{"class":768},[738,2312,2313],{"class":781}," ($status ",[738,2315,1597],{"class":768},[738,2317,1030],{"class":748},[738,2319,1436],{"class":781},[738,2321,2322,2325,2327,2329,2331,2334,2336,2338,2340,2342],{"class":740,"line":1726},[738,2323,2324],{"class":781},"                $wait ",[738,2326,1452],{"class":768},[738,2328,1330],{"class":781},[738,2330,2132],{"class":768},[738,2332,2333],{"class":781},") ($headers[",[738,2335,1625],{"class":755},[738,2337,2222],{"class":781},[738,2339,1630],{"class":768},[738,2341,1633],{"class":748},[738,2343,1636],{"class":781},[738,2345,2346,2349,2351,2353,2355,2358,2360,2363,2365,2367,2369,2371,2373,2375,2377,2379,2381],{"class":740,"line":1748},[738,2347,2348],{"class":748},"                $this",[738,2350,2190],{"class":768},[738,2352,2193],{"class":781},[738,2354,1452],{"class":768},[738,2356,2357],{"class":781}," [",[738,2359,2219],{"class":755},[738,2361,2362],{"class":768}," =>",[738,2364,1510],{"class":748},[738,2366,621],{"class":781},[738,2368,2245],{"class":755},[738,2370,2362],{"class":768},[738,2372,2231],{"class":748},[738,2374,1415],{"class":781},[738,2376,2236],{"class":748},[738,2378,1336],{"class":781},[738,2380,1660],{"class":768},[738,2382,2383],{"class":781}," $wait];\n",[738,2385,2386,2388,2391,2393,2395,2397,2399,2402,2404,2406,2408,2411],{"class":740,"line":1769},[738,2387,2253],{"class":748},[738,2389,2390],{"class":781},"(($wait ",[738,2392,1666],{"class":768},[738,2394,2280],{"class":748},[738,2396,1336],{"class":781},[738,2398,1660],{"class":768},[738,2400,2401],{"class":1323}," random_int",[738,2403,1415],{"class":781},[738,2405,717],{"class":748},[738,2407,621],{"class":781},[738,2409,2410],{"class":748},"2_000_000",[738,2412,2283],{"class":781},[738,2414,2415,2418],{"class":740,"line":1794},[738,2416,2417],{"class":768},"                continue",[738,2419,1713],{"class":781},[738,2421,2422],{"class":740,"line":1804},[738,2423,2288],{"class":781},[738,2425,2426,2428,2430,2433,2436,2438,2441,2443],{"class":740,"line":1816},[738,2427,2204],{"class":768},[738,2429,1330],{"class":781},[738,2431,2432],{"class":748},"isset",[738,2434,2435],{"class":781},"($headers[",[738,2437,1743],{"class":755},[738,2439,2440],{"class":781},"], $headers[",[738,2442,1764],{"class":755},[738,2444,2445],{"class":781},"])) {\n",[738,2447,2448,2450,2452,2454,2456],{"class":740,"line":1839},[738,2449,2348],{"class":748},[738,2451,2190],{"class":768},[738,2453,2193],{"class":781},[738,2455,1452],{"class":768},[738,2457,2458],{"class":781}," [\n",[738,2460,2461,2464,2466,2468,2470,2473,2475],{"class":740,"line":1845},[738,2462,2463],{"class":755},"                    'remaining'",[738,2465,2362],{"class":768},[738,2467,1330],{"class":781},[738,2469,2132],{"class":768},[738,2471,2472],{"class":781},") $headers[",[738,2474,1743],{"class":755},[738,2476,2477],{"class":781},"],\n",[738,2479,2480,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501,2503],{"class":740,"line":1850},[738,2481,2482],{"class":755},"                    'resetAt'",[738,2484,2362],{"class":768},[738,2486,2231],{"class":748},[738,2488,1415],{"class":781},[738,2490,2236],{"class":748},[738,2492,1336],{"class":781},[738,2494,1660],{"class":768},[738,2496,1330],{"class":781},[738,2498,2132],{"class":768},[738,2500,2472],{"class":781},[738,2502,1764],{"class":755},[738,2504,2477],{"class":781},[738,2506,2507],{"class":740,"line":1859},[738,2508,2509],{"class":781},"                ];\n",[738,2511,2512],{"class":740,"line":1864},[738,2513,2288],{"class":781},[738,2515,2516,2519],{"class":740,"line":1892},[738,2517,2518],{"class":768},"            return",[738,2520,2521],{"class":781}," [$status, $headers, $body];\n",[738,2523,2524],{"class":740,"line":1897},[738,2525,2526],{"class":781},"        }\n",[738,2528,2529,2532,2534,2537,2539,2542,2545,2548,2551,2554],{"class":740,"line":1902},[738,2530,2531],{"class":768},"        throw",[738,2533,1342],{"class":768},[738,2535,2536],{"class":748}," RuntimeException",[738,2538,1415],{"class":781},[738,2540,2541],{"class":755},"\"Still rate-limited after {",[738,2543,2544],{"class":781},"$maxAttempts",[738,2546,2547],{"class":755},"} attempts: {",[738,2549,2550],{"class":781},"$operation",[738,2552,2553],{"class":755},"}\"",[738,2555,1636],{"class":781},[738,2557,2558],{"class":740,"line":1908},[738,2559,1555],{"class":781},[738,2561,2562],{"class":740,"line":1932},[738,2563,1139],{"class":781},[357,2565,2566],{},"The gate lives in one process. If several processes share one account, give each a share of the budget, or send all calls to one operation through a single queue.",[361,2568,2570],{"id":2569},"capacity-planning","Capacity planning",[357,2572,2573],{},"At 100 requests per 60 seconds per operation:",[560,2575,2576,2589],{},[563,2577,2578],{},[566,2579,2580,2583,2586],{},[569,2581,2582],{},"Task",[569,2584,2585],{},"Operation",[569,2587,2588],{},"Most per minute",[576,2590,2591,2604,2616,2629,2641,2654],{},[566,2592,2593,2596,2601],{},[581,2594,2595],{},"Register students",[581,2597,2598],{},[379,2599,2600],{"href":174},"registerStudent",[581,2602,2603],{},"100 registrations",[566,2605,2606,2609,2613],{},[581,2607,2608],{},"Register a class at a time",[581,2610,2611],{},[379,2612,397],{"href":180},[581,2614,2615],{},"10,000 registrations an hour, in batches of up to 1000, one batch at a time",[566,2617,2618,2621,2626],{},[581,2619,2620],{},"Send students into the panel",[581,2622,2623],{},[379,2624,2625],{"href":211},"createSigninLink",[581,2627,2628],{},"100 links, across all organizations together",[566,2630,2631,2634,2638],{},[581,2632,2633],{},"Enter students for exams",[581,2635,2636],{},[379,2637,477],{"href":238},[581,2639,2640],{},"100 applications, across all organizations together",[566,2642,2643,2648,2651],{},[581,2644,2645,2646],{},"Read any list, at ",[403,2647,1271],{},[581,2649,2650],{},"Any list operation",[581,2652,2653],{},"10,000 records",[566,2655,2656,2659,2664],{},[581,2657,2658],{},"Download certificates",[581,2660,2661],{},[379,2662,2663],{"href":261},"downloadCertificate",[581,2665,2666],{},"100 files",[366,2668,2669],{"type":368},[357,2670,2671,2674,2675,414],{},[372,2672,2673],{},"Sign-in links are created on demand, one per click, and each is capped like any other operation."," If a large group of students clicks \"Go to my panel\" in the same minute, your account can create at most 100 links in that minute. Links are single-use and valid for 120 seconds, so you cannot create them ahead of time. If your portal expects bursts, queue the clicks and show the student a short \"preparing your link\" state while you wait. See ",[379,2676,80],{"href":81},[357,2678,2679,2680,2682],{},"A worked example: a nightly job for 3,000 students on two organizations lists each student's certificates (6,000 calls to one operation, since the two organizations share its counter), then downloads the new files (a different operation, with its own counter). The listing alone needs at least 60 minutes. The downloads can run alongside it without slowing it down. ",[379,2681,109],{"href":110}," builds this job step by step.",[361,2684,2686],{"id":2685},"common-questions","Common questions",[357,2688,2689,2692,2693,2697],{},[372,2690,2691],{},"Can my account get a higher limit?","\nThe limit is the same for every account. If your volumes do not fit, write to ",[379,2694,2696],{"href":2695},"mailto:info@main-team.org","info@main-team.org"," with your numbers, and plan your jobs around the current limit in the meantime.",[357,2699,2700,2703,2704,621,2706,474,2708,2710,2711,2714,2715,474,2718,2720,2721,414],{},[372,2701,2702],{},"Do failed requests count?","\nOnly if they got past the token, organization and permission checks. A ",[403,2705,597],{},[403,2707,601],{},[403,2709,430],{}," from the operation itself counts; a ",[403,2712,2713],{},"401",", a ",[403,2716,2717],{},"403",[403,2719,624],{}," does not. See ",[379,2722,555],{"href":2723},"#what-counts",[357,2725,2726,2729],{},[372,2727,2728],{},"Is the limit per IP address?","\nNo. Your budget is per API account. The network in front of the API also caps how fast any one client address may send, with or without a token, but a steadily paced integration does not reach it.",[357,2731,2732,2735,2736,2738,2739,414],{},[372,2733,2734],{},"Does the health check count?","\nNo. No account's budget covers ",[403,2737,653],{},", so it never uses yours. The per-address limit in front of the API still applies, so poll it at a steady interval. See ",[379,2740,16],{"href":17},[2742,2743,2744],"style",{},"html pre.shiki code .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .sT6z2, html code.shiki .sT6z2{--shiki-default:#032563;--shiki-dark:#ADDCFF}html pre.shiki code .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .suds8, html code.shiki .suds8{--shiki-default:#0E1116;--shiki-dark:#F0F3F6}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);}html pre.shiki code .sne4z, html code.shiki .sne4z{--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .sKwhi, html code.shiki .sKwhi{--shiki-default:#622CBC;--shiki-dark:#DBB7FF}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}",{"title":734,"searchDepth":772,"depth":823,"links":2746},[2747,2750,2751,2752,2753,2754,2755,2756,2757,2758],{"id":363,"depth":772,"text":364,"children":2748},[2749],{"id":387,"depth":823,"text":388},{"id":466,"depth":772,"text":467},{"id":547,"depth":772,"text":548},{"id":554,"depth":772,"text":555},{"id":668,"depth":772,"text":669},{"id":867,"depth":772,"text":868},{"id":979,"depth":772,"text":980},{"id":1229,"depth":772,"text":1230},{"id":2569,"depth":772,"text":2570},{"id":2685,"depth":772,"text":2686},"How many requests your account may make, how they are counted, the X-RateLimit and Retry-After headers, what a 429 costs you, and how to stay under the limit.","md",{},[],{"title":45,"description":2759},"api\u002Frate-limits","MqERL_vgCYlZ7zJIzYbrOmgQBkth60rK8uzvraaoEk8",[],1791554614937]