[{"data":1,"prerenderedAt":1257},["ShallowReactive",2],{"api-rate-limits":3},{"article":4,"sections":1221,"prevSection":1250,"nextSection":1254},{"id":5,"title":6,"avatar":7,"body":8,"description":1214,"docx":7,"extension":1215,"meta":1216,"navigation":192,"order":310,"path":1217,"pdf":7,"pdfLabel":7,"procurementPack":7,"seo":1218,"slug":14,"stem":1219,"__hash__":1220},"content\u002Fapi\u002Frate-limits.md","Rate Limits",null,{"type":9,"value":10,"toc":1189},"minimark",[11,15,19,24,79,88,92,95,139,144,154,158,161,248,252,256,330,334,428,432,663,667,671,674,712,716,719,794,798,801,897,901,904,989,993,996,1000,1024,1027,1034,1059,1063,1142,1146,1171,1175,1178,1182,1185],[12,13,6],"h1",{"id":14},"rate-limits",[16,17,18],"p",{},"The HeadshotPro API implements rate limiting to ensure fair usage and maintain service reliability.",[20,21,23],"h2",{"id":22},"rate-limit-tiers","Rate Limit Tiers",[25,26,27,43],"table",{},[28,29,30],"thead",{},[31,32,33,37,40],"tr",{},[34,35,36],"th",{},"Tier",[34,38,39],{},"Requests per Minute",[34,41,42],{},"Description",[44,45,46,58,68],"tbody",{},[31,47,48,52,55],{},[49,50,51],"td",{},"Standard",[49,53,54],{},"300",[49,56,57],{},"Default for all organizations",[31,59,60,63,65],{},[49,61,62],{},"Professional",[49,64,54],{},[49,66,67],{},"Standard commercial plans",[31,69,70,73,76],{},[49,71,72],{},"Enterprise",[49,74,75],{},"1,000",[49,77,78],{},"High-volume enterprise plans",[16,80,81,82,87],{},"Contact ",[83,84,86],"a",{"href":85},"mailto:sales@headshotpro.com","sales@headshotpro.com"," to upgrade your rate limit tier.",[20,89,91],{"id":90},"rate-limit-headers","Rate Limit Headers",[16,93,94],{},"Every API response includes rate limit information in the headers:",[25,96,97,106],{},[28,98,99],{},[31,100,101,104],{},[34,102,103],{},"Header",[34,105,42],{},[44,107,108,119,129],{},[31,109,110,116],{},[49,111,112],{},[113,114,115],"code",{},"X-RateLimit-Limit",[49,117,118],{},"Maximum requests allowed per minute",[31,120,121,126],{},[49,122,123],{},[113,124,125],{},"X-RateLimit-Remaining",[49,127,128],{},"Requests remaining in current window",[31,130,131,136],{},[49,132,133],{},[113,134,135],{},"X-RateLimit-Reset",[49,137,138],{},"ISO timestamp when the limit resets",[140,141,143],"h3",{"id":142},"example-headers","Example Headers",[145,146,151],"pre",{"className":147,"code":149,"language":150},[148],"language-text","X-RateLimit-Limit: 300\nX-RateLimit-Remaining: 295\nX-RateLimit-Reset: 2024-01-15T10:31:00.000Z\n","text",[113,152,149],{"__ignoreMap":153},"",[20,155,157],{"id":156},"rate-limit-exceeded","Rate Limit Exceeded",[16,159,160],{},"When you exceed your rate limit, the API returns:",[145,162,166],{"className":163,"code":164,"language":165,"meta":153,"style":153},"language-json shiki shiki-themes dark-plus","HTTP\u002F1.1 429 Too Many Requests\n\n{\n  \"success\": false,\n  \"error\": \"Rate limit exceeded\",\n  \"code\": \"RATE_LIMIT_EXCEEDED\"\n}\n","json",[113,167,168,187,194,200,217,231,242],{"__ignoreMap":153},[169,170,173,177,181,184],"span",{"class":171,"line":172},"line",1,[169,174,176],{"class":175},"sTNBD","HTTP\u002F",[169,178,180],{"class":179},"sDGX8","1.1",[169,182,183],{"class":179}," 429",[169,185,186],{"class":175}," Too Many Requests\n",[169,188,190],{"class":171,"line":189},2,[169,191,193],{"emptyLinePlaceholder":192},true,"\n",[169,195,197],{"class":171,"line":196},3,[169,198,199],{"class":175},"{\n",[169,201,203,207,210,214],{"class":171,"line":202},4,[169,204,206],{"class":205},"s3F5K","  \"success\"",[169,208,209],{"class":175},": ",[169,211,213],{"class":212},"scz_3","false",[169,215,216],{"class":175},",\n",[169,218,220,223,225,229],{"class":171,"line":219},5,[169,221,222],{"class":205},"  \"error\"",[169,224,209],{"class":175},[169,226,228],{"class":227},"sKc5r","\"Rate limit exceeded\"",[169,230,216],{"class":175},[169,232,234,237,239],{"class":171,"line":233},6,[169,235,236],{"class":205},"  \"code\"",[169,238,209],{"class":175},[169,240,241],{"class":227},"\"RATE_LIMIT_EXCEEDED\"\n",[169,243,245],{"class":171,"line":244},7,[169,246,247],{"class":175},"}\n",[20,249,251],{"id":250},"handling-rate-limits","Handling Rate Limits",[140,253,255],{"id":254},"_1-monitor-headers","1. Monitor Headers",[145,257,261],{"className":258,"code":259,"language":260,"meta":153,"style":153},"language-javascript shiki shiki-themes dark-plus","async function apiRequest(url, options) {\n  const response = await fetch(url, options);\n\n  \u002F\u002F Check remaining requests\n  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));\n  const limit = parseInt(response.headers.get('X-RateLimit-Limit'));\n\n  if (remaining \u003C limit * 0.1) {\n    console.warn(`Rate limit warning: ${remaining}\u002F${limit} requests remaining`);\n  }\n\n  return response;\n}\n","javascript",[113,262,263,268,273,277,282,287,292,296,302,308,314,319,325],{"__ignoreMap":153},[169,264,265],{"class":171,"line":172},[169,266,267],{},"async function apiRequest(url, options) {\n",[169,269,270],{"class":171,"line":189},[169,271,272],{},"  const response = await fetch(url, options);\n",[169,274,275],{"class":171,"line":196},[169,276,193],{"emptyLinePlaceholder":192},[169,278,279],{"class":171,"line":202},[169,280,281],{},"  \u002F\u002F Check remaining requests\n",[169,283,284],{"class":171,"line":219},[169,285,286],{},"  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));\n",[169,288,289],{"class":171,"line":233},[169,290,291],{},"  const limit = parseInt(response.headers.get('X-RateLimit-Limit'));\n",[169,293,294],{"class":171,"line":244},[169,295,193],{"emptyLinePlaceholder":192},[169,297,299],{"class":171,"line":298},8,[169,300,301],{},"  if (remaining \u003C limit * 0.1) {\n",[169,303,305],{"class":171,"line":304},9,[169,306,307],{},"    console.warn(`Rate limit warning: ${remaining}\u002F${limit} requests remaining`);\n",[169,309,311],{"class":171,"line":310},10,[169,312,313],{},"  }\n",[169,315,317],{"class":171,"line":316},11,[169,318,193],{"emptyLinePlaceholder":192},[169,320,322],{"class":171,"line":321},12,[169,323,324],{},"  return response;\n",[169,326,328],{"class":171,"line":327},13,[169,329,247],{},[140,331,333],{"id":332},"_2-implement-backoff","2. Implement Backoff",[145,335,337],{"className":258,"code":336,"language":260,"meta":153,"style":153},"async function apiRequestWithRetry(url, options, maxRetries = 3) {\n  for (let attempt = 0; attempt \u003C maxRetries; attempt++) {\n    const response = await fetch(url, options);\n\n    if (response.status === 429) {\n      const resetTime = new Date(response.headers.get('X-RateLimit-Reset'));\n      const waitTime = Math.max(0, resetTime - new Date());\n\n      console.log(`Rate limited. Waiting ${waitTime}ms...`);\n      await new Promise(resolve => setTimeout(resolve, waitTime + 100));\n      continue;\n    }\n\n    return response;\n  }\n\n  throw new Error('Rate limit retry attempts exhausted');\n}\n",[113,338,339,344,349,354,358,363,368,373,377,382,387,392,397,401,407,412,417,423],{"__ignoreMap":153},[169,340,341],{"class":171,"line":172},[169,342,343],{},"async function apiRequestWithRetry(url, options, maxRetries = 3) {\n",[169,345,346],{"class":171,"line":189},[169,347,348],{},"  for (let attempt = 0; attempt \u003C maxRetries; attempt++) {\n",[169,350,351],{"class":171,"line":196},[169,352,353],{},"    const response = await fetch(url, options);\n",[169,355,356],{"class":171,"line":202},[169,357,193],{"emptyLinePlaceholder":192},[169,359,360],{"class":171,"line":219},[169,361,362],{},"    if (response.status === 429) {\n",[169,364,365],{"class":171,"line":233},[169,366,367],{},"      const resetTime = new Date(response.headers.get('X-RateLimit-Reset'));\n",[169,369,370],{"class":171,"line":244},[169,371,372],{},"      const waitTime = Math.max(0, resetTime - new Date());\n",[169,374,375],{"class":171,"line":298},[169,376,193],{"emptyLinePlaceholder":192},[169,378,379],{"class":171,"line":304},[169,380,381],{},"      console.log(`Rate limited. Waiting ${waitTime}ms...`);\n",[169,383,384],{"class":171,"line":310},[169,385,386],{},"      await new Promise(resolve => setTimeout(resolve, waitTime + 100));\n",[169,388,389],{"class":171,"line":316},[169,390,391],{},"      continue;\n",[169,393,394],{"class":171,"line":321},[169,395,396],{},"    }\n",[169,398,399],{"class":171,"line":327},[169,400,193],{"emptyLinePlaceholder":192},[169,402,404],{"class":171,"line":403},14,[169,405,406],{},"    return response;\n",[169,408,410],{"class":171,"line":409},15,[169,411,313],{},[169,413,415],{"class":171,"line":414},16,[169,416,193],{"emptyLinePlaceholder":192},[169,418,420],{"class":171,"line":419},17,[169,421,422],{},"  throw new Error('Rate limit retry attempts exhausted');\n",[169,424,426],{"class":171,"line":425},18,[169,427,247],{},[140,429,431],{"id":430},"_3-queue-requests","3. Queue Requests",[145,433,435],{"className":258,"code":434,"language":260,"meta":153,"style":153},"class RateLimitedQueue {\n  constructor(requestsPerMinute = 300) {\n    this.queue = [];\n    this.processing = false;\n    this.interval = 60000 \u002F requestsPerMinute;\n  }\n\n  async add(requestFn) {\n    return new Promise((resolve, reject) => {\n      this.queue.push({ requestFn, resolve, reject });\n      this.process();\n    });\n  }\n\n  async process() {\n    if (this.processing || this.queue.length === 0) return;\n    this.processing = true;\n\n    while (this.queue.length > 0) {\n      const { requestFn, resolve, reject } = this.queue.shift();\n\n      try {\n        const result = await requestFn();\n        resolve(result);\n      } catch (error) {\n        reject(error);\n      }\n\n      await new Promise(r => setTimeout(r, this.interval));\n    }\n\n    this.processing = false;\n  }\n}\n\n\u002F\u002F Usage\nconst queue = new RateLimitedQueue(300);\n\nconst results = await Promise.all(\n  emails.map(email =>\n    queue.add(() => createInvite(email))\n  )\n);\n",[113,436,437,442,447,452,457,462,466,470,475,480,485,490,495,499,503,508,513,518,522,528,534,539,545,551,557,563,569,575,580,586,591,596,601,606,611,616,622,628,633,639,645,651,657],{"__ignoreMap":153},[169,438,439],{"class":171,"line":172},[169,440,441],{},"class RateLimitedQueue {\n",[169,443,444],{"class":171,"line":189},[169,445,446],{},"  constructor(requestsPerMinute = 300) {\n",[169,448,449],{"class":171,"line":196},[169,450,451],{},"    this.queue = [];\n",[169,453,454],{"class":171,"line":202},[169,455,456],{},"    this.processing = false;\n",[169,458,459],{"class":171,"line":219},[169,460,461],{},"    this.interval = 60000 \u002F requestsPerMinute;\n",[169,463,464],{"class":171,"line":233},[169,465,313],{},[169,467,468],{"class":171,"line":244},[169,469,193],{"emptyLinePlaceholder":192},[169,471,472],{"class":171,"line":298},[169,473,474],{},"  async add(requestFn) {\n",[169,476,477],{"class":171,"line":304},[169,478,479],{},"    return new Promise((resolve, reject) => {\n",[169,481,482],{"class":171,"line":310},[169,483,484],{},"      this.queue.push({ requestFn, resolve, reject });\n",[169,486,487],{"class":171,"line":316},[169,488,489],{},"      this.process();\n",[169,491,492],{"class":171,"line":321},[169,493,494],{},"    });\n",[169,496,497],{"class":171,"line":327},[169,498,313],{},[169,500,501],{"class":171,"line":403},[169,502,193],{"emptyLinePlaceholder":192},[169,504,505],{"class":171,"line":409},[169,506,507],{},"  async process() {\n",[169,509,510],{"class":171,"line":414},[169,511,512],{},"    if (this.processing || this.queue.length === 0) return;\n",[169,514,515],{"class":171,"line":419},[169,516,517],{},"    this.processing = true;\n",[169,519,520],{"class":171,"line":425},[169,521,193],{"emptyLinePlaceholder":192},[169,523,525],{"class":171,"line":524},19,[169,526,527],{},"    while (this.queue.length > 0) {\n",[169,529,531],{"class":171,"line":530},20,[169,532,533],{},"      const { requestFn, resolve, reject } = this.queue.shift();\n",[169,535,537],{"class":171,"line":536},21,[169,538,193],{"emptyLinePlaceholder":192},[169,540,542],{"class":171,"line":541},22,[169,543,544],{},"      try {\n",[169,546,548],{"class":171,"line":547},23,[169,549,550],{},"        const result = await requestFn();\n",[169,552,554],{"class":171,"line":553},24,[169,555,556],{},"        resolve(result);\n",[169,558,560],{"class":171,"line":559},25,[169,561,562],{},"      } catch (error) {\n",[169,564,566],{"class":171,"line":565},26,[169,567,568],{},"        reject(error);\n",[169,570,572],{"class":171,"line":571},27,[169,573,574],{},"      }\n",[169,576,578],{"class":171,"line":577},28,[169,579,193],{"emptyLinePlaceholder":192},[169,581,583],{"class":171,"line":582},29,[169,584,585],{},"      await new Promise(r => setTimeout(r, this.interval));\n",[169,587,589],{"class":171,"line":588},30,[169,590,396],{},[169,592,594],{"class":171,"line":593},31,[169,595,193],{"emptyLinePlaceholder":192},[169,597,599],{"class":171,"line":598},32,[169,600,456],{},[169,602,604],{"class":171,"line":603},33,[169,605,313],{},[169,607,609],{"class":171,"line":608},34,[169,610,247],{},[169,612,614],{"class":171,"line":613},35,[169,615,193],{"emptyLinePlaceholder":192},[169,617,619],{"class":171,"line":618},36,[169,620,621],{},"\u002F\u002F Usage\n",[169,623,625],{"class":171,"line":624},37,[169,626,627],{},"const queue = new RateLimitedQueue(300);\n",[169,629,631],{"class":171,"line":630},38,[169,632,193],{"emptyLinePlaceholder":192},[169,634,636],{"class":171,"line":635},39,[169,637,638],{},"const results = await Promise.all(\n",[169,640,642],{"class":171,"line":641},40,[169,643,644],{},"  emails.map(email =>\n",[169,646,648],{"class":171,"line":647},41,[169,649,650],{},"    queue.add(() => createInvite(email))\n",[169,652,654],{"class":171,"line":653},42,[169,655,656],{},"  )\n",[169,658,660],{"class":171,"line":659},43,[169,661,662],{},");\n",[20,664,666],{"id":665},"best-practices","Best Practices",[140,668,670],{"id":669},"batch-operations","Batch Operations",[16,672,673],{},"Instead of making individual requests, use batch endpoints where available:",[145,675,677],{"className":258,"code":676,"language":260,"meta":153,"style":153},"\u002F\u002F Instead of 100 individual requests:\nfor (const email of emails) {\n  await createInvite(email);\n}\n\n\u002F\u002F Use batch operations where possible:\nawait addTeamMembers(teamId, emails);\n",[113,678,679,684,689,694,698,702,707],{"__ignoreMap":153},[169,680,681],{"class":171,"line":172},[169,682,683],{},"\u002F\u002F Instead of 100 individual requests:\n",[169,685,686],{"class":171,"line":189},[169,687,688],{},"for (const email of emails) {\n",[169,690,691],{"class":171,"line":196},[169,692,693],{},"  await createInvite(email);\n",[169,695,696],{"class":171,"line":202},[169,697,247],{},[169,699,700],{"class":171,"line":219},[169,701,193],{"emptyLinePlaceholder":192},[169,703,704],{"class":171,"line":233},[169,705,706],{},"\u002F\u002F Use batch operations where possible:\n",[169,708,709],{"class":171,"line":244},[169,710,711],{},"await addTeamMembers(teamId, emails);\n",[140,713,715],{"id":714},"caching","Caching",[16,717,718],{},"Cache responses to reduce API calls:",[145,720,722],{"className":258,"code":721,"language":260,"meta":153,"style":153},"const cache = new Map();\nconst CACHE_TTL = 5 * 60 * 1000; \u002F\u002F 5 minutes\n\nasync function getOrganization() {\n  const cached = cache.get('organization');\n  if (cached && Date.now() - cached.timestamp \u003C CACHE_TTL) {\n    return cached.data;\n  }\n\n  const response = await fetch('\u002Fapi\u002Fv2\u002Forganization', { ... });\n  const data = await response.json();\n\n  cache.set('organization', { data, timestamp: Date.now() });\n  return data;\n}\n",[113,723,724,729,734,738,743,748,753,758,762,766,771,776,780,785,790],{"__ignoreMap":153},[169,725,726],{"class":171,"line":172},[169,727,728],{},"const cache = new Map();\n",[169,730,731],{"class":171,"line":189},[169,732,733],{},"const CACHE_TTL = 5 * 60 * 1000; \u002F\u002F 5 minutes\n",[169,735,736],{"class":171,"line":196},[169,737,193],{"emptyLinePlaceholder":192},[169,739,740],{"class":171,"line":202},[169,741,742],{},"async function getOrganization() {\n",[169,744,745],{"class":171,"line":219},[169,746,747],{},"  const cached = cache.get('organization');\n",[169,749,750],{"class":171,"line":233},[169,751,752],{},"  if (cached && Date.now() - cached.timestamp \u003C CACHE_TTL) {\n",[169,754,755],{"class":171,"line":244},[169,756,757],{},"    return cached.data;\n",[169,759,760],{"class":171,"line":298},[169,761,313],{},[169,763,764],{"class":171,"line":304},[169,765,193],{"emptyLinePlaceholder":192},[169,767,768],{"class":171,"line":310},[169,769,770],{},"  const response = await fetch('\u002Fapi\u002Fv2\u002Forganization', { ... });\n",[169,772,773],{"class":171,"line":316},[169,774,775],{},"  const data = await response.json();\n",[169,777,778],{"class":171,"line":321},[169,779,193],{"emptyLinePlaceholder":192},[169,781,782],{"class":171,"line":327},[169,783,784],{},"  cache.set('organization', { data, timestamp: Date.now() });\n",[169,786,787],{"class":171,"line":403},[169,788,789],{},"  return data;\n",[169,791,792],{"class":171,"line":409},[169,793,247],{},[140,795,797],{"id":796},"efficient-polling","Efficient Polling",[16,799,800],{},"When polling for status updates, use exponential backoff:",[145,802,804],{"className":258,"code":803,"language":260,"meta":153,"style":153},"async function pollModelStatus(modelId, maxAttempts = 60) {\n  let delay = 5000; \u002F\u002F Start with 5 seconds\n\n  for (let attempt = 0; attempt \u003C maxAttempts; attempt++) {\n    const { model } = await getModel(modelId);\n\n    if (model.status === 'active') {\n      return model;\n    }\n\n    if (model.status === 'failed') {\n      throw new Error('Model processing failed');\n    }\n\n    await new Promise(r => setTimeout(r, delay));\n    delay = Math.min(delay * 1.5, 60000); \u002F\u002F Max 1 minute\n  }\n\n  throw new Error('Polling timeout');\n}\n",[113,805,806,811,816,820,825,830,834,839,844,848,852,857,862,866,870,875,880,884,888,893],{"__ignoreMap":153},[169,807,808],{"class":171,"line":172},[169,809,810],{},"async function pollModelStatus(modelId, maxAttempts = 60) {\n",[169,812,813],{"class":171,"line":189},[169,814,815],{},"  let delay = 5000; \u002F\u002F Start with 5 seconds\n",[169,817,818],{"class":171,"line":196},[169,819,193],{"emptyLinePlaceholder":192},[169,821,822],{"class":171,"line":202},[169,823,824],{},"  for (let attempt = 0; attempt \u003C maxAttempts; attempt++) {\n",[169,826,827],{"class":171,"line":219},[169,828,829],{},"    const { model } = await getModel(modelId);\n",[169,831,832],{"class":171,"line":233},[169,833,193],{"emptyLinePlaceholder":192},[169,835,836],{"class":171,"line":244},[169,837,838],{},"    if (model.status === 'active') {\n",[169,840,841],{"class":171,"line":298},[169,842,843],{},"      return model;\n",[169,845,846],{"class":171,"line":304},[169,847,396],{},[169,849,850],{"class":171,"line":310},[169,851,193],{"emptyLinePlaceholder":192},[169,853,854],{"class":171,"line":316},[169,855,856],{},"    if (model.status === 'failed') {\n",[169,858,859],{"class":171,"line":321},[169,860,861],{},"      throw new Error('Model processing failed');\n",[169,863,864],{"class":171,"line":327},[169,865,396],{},[169,867,868],{"class":171,"line":403},[169,869,193],{"emptyLinePlaceholder":192},[169,871,872],{"class":171,"line":409},[169,873,874],{},"    await new Promise(r => setTimeout(r, delay));\n",[169,876,877],{"class":171,"line":414},[169,878,879],{},"    delay = Math.min(delay * 1.5, 60000); \u002F\u002F Max 1 minute\n",[169,881,882],{"class":171,"line":419},[169,883,313],{},[169,885,886],{"class":171,"line":425},[169,887,193],{"emptyLinePlaceholder":192},[169,889,890],{"class":171,"line":524},[169,891,892],{},"  throw new Error('Polling timeout');\n",[169,894,895],{"class":171,"line":530},[169,896,247],{},[140,898,900],{"id":899},"use-webhooks","Use Webhooks",[16,902,903],{},"Instead of polling, configure webhooks to receive real-time notifications:",[145,905,907],{"className":258,"code":906,"language":260,"meta":153,"style":153},"\u002F\u002F Instead of polling:\nwhile (model.status !== 'active') {\n  await sleep(5000);\n  model = await getModel(modelId);\n}\n\n\u002F\u002F Use webhooks:\napp.post('\u002Fwebhooks\u002Fheadshotpro', (req, res) => {\n  const { event, object } = req.body;\n\n  if (event === 'model.photos_ready') {\n    \u002F\u002F Photos are ready, fetch them\n    processCompletedModel(object._id);\n  }\n\n  res.status(200).send('OK');\n});\n",[113,908,909,914,919,924,929,933,937,942,947,952,956,961,966,971,975,979,984],{"__ignoreMap":153},[169,910,911],{"class":171,"line":172},[169,912,913],{},"\u002F\u002F Instead of polling:\n",[169,915,916],{"class":171,"line":189},[169,917,918],{},"while (model.status !== 'active') {\n",[169,920,921],{"class":171,"line":196},[169,922,923],{},"  await sleep(5000);\n",[169,925,926],{"class":171,"line":202},[169,927,928],{},"  model = await getModel(modelId);\n",[169,930,931],{"class":171,"line":219},[169,932,247],{},[169,934,935],{"class":171,"line":233},[169,936,193],{"emptyLinePlaceholder":192},[169,938,939],{"class":171,"line":244},[169,940,941],{},"\u002F\u002F Use webhooks:\n",[169,943,944],{"class":171,"line":298},[169,945,946],{},"app.post('\u002Fwebhooks\u002Fheadshotpro', (req, res) => {\n",[169,948,949],{"class":171,"line":304},[169,950,951],{},"  const { event, object } = req.body;\n",[169,953,954],{"class":171,"line":310},[169,955,193],{"emptyLinePlaceholder":192},[169,957,958],{"class":171,"line":316},[169,959,960],{},"  if (event === 'model.photos_ready') {\n",[169,962,963],{"class":171,"line":321},[169,964,965],{},"    \u002F\u002F Photos are ready, fetch them\n",[169,967,968],{"class":171,"line":327},[169,969,970],{},"    processCompletedModel(object._id);\n",[169,972,973],{"class":171,"line":403},[169,974,313],{},[169,976,977],{"class":171,"line":409},[169,978,193],{"emptyLinePlaceholder":192},[169,980,981],{"class":171,"line":414},[169,982,983],{},"  res.status(200).send('OK');\n",[169,985,986],{"class":171,"line":419},[169,987,988],{},"});\n",[20,990,992],{"id":991},"invite-limits","Invite Limits",[16,994,995],{},"In addition to API rate limits, invites have a separate spam protection limit to prevent abuse.",[140,997,999],{"id":998},"pending-invite-cap","Pending Invite Cap",[25,1001,1002,1012],{},[28,1003,1004],{},[31,1005,1006,1009],{},[34,1007,1008],{},"Metric",[34,1010,1011],{},"Formula",[44,1013,1014],{},[31,1015,1016,1019],{},[49,1017,1018],{},"Max pending invites",[49,1020,1021],{},[113,1022,1023],{},"credits × 2",[16,1025,1026],{},"For example, if your organization has 10 credits, you can have up to 20 pending invites at a time.",[16,1028,1029,1033],{},[1030,1031,1032],"strong",{},"Pending invites"," include:",[1035,1036,1037,1041],"ul",{},[1038,1039,1040],"li",{},"V1 invites that are valid but not yet used",[1038,1042,1043,1044,1047,1048,1047,1051,1054,1055,1058],{},"Models in ",[113,1045,1046],{},"onboarding",", ",[113,1049,1050],{},"pending",[113,1052,1053],{},"waiting",", or ",[113,1056,1057],{},"generatingHeadshots"," status",[140,1060,1062],{"id":1061},"when-limit-is-exceeded","When Limit Is Exceeded",[145,1064,1066],{"className":163,"code":1065,"language":165,"meta":153,"style":153},"{\n  \"success\": false,\n  \"error\": \"Too many pending invites. You have 20 pending invites and 10 credits. Wait for invites to be completed or revoke unused invites.\",\n  \"code\": \"INVITE_LIMIT_EXCEEDED\",\n  \"pendingInvites\": 20,\n  \"credits\": 10,\n  \"maxPendingInvites\": 20\n}\n",[113,1067,1068,1072,1082,1093,1104,1116,1128,1138],{"__ignoreMap":153},[169,1069,1070],{"class":171,"line":172},[169,1071,199],{"class":175},[169,1073,1074,1076,1078,1080],{"class":171,"line":189},[169,1075,206],{"class":205},[169,1077,209],{"class":175},[169,1079,213],{"class":212},[169,1081,216],{"class":175},[169,1083,1084,1086,1088,1091],{"class":171,"line":196},[169,1085,222],{"class":205},[169,1087,209],{"class":175},[169,1089,1090],{"class":227},"\"Too many pending invites. You have 20 pending invites and 10 credits. Wait for invites to be completed or revoke unused invites.\"",[169,1092,216],{"class":175},[169,1094,1095,1097,1099,1102],{"class":171,"line":202},[169,1096,236],{"class":205},[169,1098,209],{"class":175},[169,1100,1101],{"class":227},"\"INVITE_LIMIT_EXCEEDED\"",[169,1103,216],{"class":175},[169,1105,1106,1109,1111,1114],{"class":171,"line":219},[169,1107,1108],{"class":205},"  \"pendingInvites\"",[169,1110,209],{"class":175},[169,1112,1113],{"class":179},"20",[169,1115,216],{"class":175},[169,1117,1118,1121,1123,1126],{"class":171,"line":233},[169,1119,1120],{"class":205},"  \"credits\"",[169,1122,209],{"class":175},[169,1124,1125],{"class":179},"10",[169,1127,216],{"class":175},[169,1129,1130,1133,1135],{"class":171,"line":244},[169,1131,1132],{"class":205},"  \"maxPendingInvites\"",[169,1134,209],{"class":175},[169,1136,1137],{"class":179},"20\n",[169,1139,1140],{"class":171,"line":298},[169,1141,247],{"class":175},[140,1143,1145],{"id":1144},"solutions","Solutions",[1147,1148,1149,1155,1165],"ol",{},[1038,1150,1151,1154],{},[1030,1152,1153],{},"Wait for completions"," - Pending invites clear when users complete their headshots",[1038,1156,1157,1160,1161,1164],{},[1030,1158,1159],{},"Revoke unused invites"," - Use ",[113,1162,1163],{},"POST \u002Forganization\u002Finvites\u002Frevoke"," to clear stale invites",[1038,1166,1167,1170],{},[1030,1168,1169],{},"Purchase more credits"," - More credits increases your pending invite cap",[20,1172,1174],{"id":1173},"rate-limit-by-endpoint","Rate Limit by Endpoint",[16,1176,1177],{},"All endpoints share the same rate limit pool. There are no per-endpoint limits.",[20,1179,1181],{"id":1180},"burst-handling","Burst Handling",[16,1183,1184],{},"The rate limiter uses a sliding window algorithm, so short bursts within the limit are allowed. However, sustained high-volume requests will trigger rate limiting.",[1186,1187,1188],"style",{},"html pre.shiki code .sTNBD, html code.shiki .sTNBD{--shiki-default:#D4D4D4}html pre.shiki code .sDGX8, html code.shiki .sDGX8{--shiki-default:#B5CEA8}html pre.shiki code .s3F5K, html code.shiki .s3F5K{--shiki-default:#9CDCFE}html pre.shiki code .scz_3, html code.shiki .scz_3{--shiki-default:#569CD6}html pre.shiki code .sKc5r, html code.shiki .sKc5r{--shiki-default:#CE9178}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);}",{"title":153,"searchDepth":189,"depth":189,"links":1190},[1191,1192,1195,1196,1201,1207,1212,1213],{"id":22,"depth":189,"text":23},{"id":90,"depth":189,"text":91,"children":1193},[1194],{"id":142,"depth":196,"text":143},{"id":156,"depth":189,"text":157},{"id":250,"depth":189,"text":251,"children":1197},[1198,1199,1200],{"id":254,"depth":196,"text":255},{"id":332,"depth":196,"text":333},{"id":430,"depth":196,"text":431},{"id":665,"depth":189,"text":666,"children":1202},[1203,1204,1205,1206],{"id":669,"depth":196,"text":670},{"id":714,"depth":196,"text":715},{"id":796,"depth":196,"text":797},{"id":899,"depth":196,"text":900},{"id":991,"depth":189,"text":992,"children":1208},[1209,1210,1211],{"id":998,"depth":196,"text":999},{"id":1061,"depth":196,"text":1062},{"id":1144,"depth":196,"text":1145},{"id":1173,"depth":189,"text":1174},{"id":1180,"depth":189,"text":1181},"API rate limiting tiers and best practices","md",{},"\u002Fapi\u002Frate-limits",{"title":6,"description":1214},"api\u002Frate-limits","TuUifM_98mhJCEi_r0-w_JO9cwZuLkMXCGlY4al6aYg",[1222,1226,1229,1232,1235,1238,1241,1244,1247,1250,1253,1254],{"title":1223,"slug":1224,"order":1225},"API Overview","index",0,{"title":1227,"slug":1228,"order":172},"Authentication","authentication",{"title":1230,"slug":1231,"order":189},"Organization","organization",{"title":1233,"slug":1234,"order":196},"Invites","invites",{"title":1236,"slug":1237,"order":202},"Teams","teams",{"title":1239,"slug":1240,"order":219},"Team Members","team-members",{"title":1242,"slug":1243,"order":233},"Models","models",{"title":1245,"slug":1246,"order":244},"Photos","photos",{"title":1248,"slug":1249,"order":298},"Whitelabel","whitelabel",{"title":1251,"slug":1252,"order":304},"Errors","errors",{"title":6,"slug":14,"order":310},{"title":1255,"slug":1256,"order":316},"Webhooks","webhooks",1792465015341]