{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Rate limiting","siteUrl":"https://docs.api.dentally.com"},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"rate-limiting","__idx":0},"children":["Rate limiting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Dentally NexGen API enforces rate limits to protect platform stability and"," ","ensure fair access for all integrations."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Understanding how limits work helps you build integrations that stay within"," ","bounds and recover gracefully when a limit is reached."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-limits-are-configured","__idx":1},"children":["How limits are configured"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Hourly quotas are set ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/pages/authentication#partner-products-and-subscriptions"},"children":["per product and per practice"]}," when your integration is onboarded."," ","Dentally may adjust limits as your integration's needs evolve."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Limit"},"children":["Limit"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Scope"},"children":["Scope"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Product quota"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your integration across ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all practices"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum API requests in a rolling one‑hour window, summed over every ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Practice-Id"]}," you call."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Subscription quota"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your integration for ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["one practice"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum API requests in a rolling one‑hour window for a single ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Practice-Id"]},"."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"spike-arrest","__idx":2},"children":["Spike arrest"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["spike arrest"]}," limits how many requests you can send in a short burst."," ","It protects the platform and prevents your hourly quota from being consumed in"," ","a few seconds of heavy concurrency."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":""},"children":[]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":""},"children":[]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Rate"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["10 requests per second"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Scope"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Per product ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["and"]}," practice — your credentials together with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Practice-Id"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each practice you call has its own spike‑arrest window. Sending 10 requests"," ","per second to practice A does not affect practice B."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When spike arrest triggers, the API responds with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429 Too Many Requests"]},"."," ","This is separate from hourly quota exhaustion; the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["detail"]}," field in the"," ","response body distinguishes the two cases (see"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#when-a-limit-is-exceeded"},"children":["When a limit is exceeded"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"hourly-quotas","__idx":3},"children":["Hourly quotas"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Up to two independent hourly quotas may apply. Both use a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["rolling one‑hour"," ","window"]},": the clock does not reset on the hour; instead, each request's"," ","timestamp falls out of the window one hour after it was made."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both quotas are evaluated on every request. Hitting either limit returns"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429 Too Many Requests"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"response-headers","__idx":4},"children":["Response headers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Successful responses (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2XX"]},") and quota‑exceeded responses (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429"]},") may include"," ","rate‑limit headers so clients can throttle proactively."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"hourly-quota-headers","__idx":5},"children":["Hourly quota headers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Present when the corresponding quota is configured for your product:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Header"},"children":["Header"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Product-Limit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum requests allowed in the current product window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Product-Remaining"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Requests remaining in the current product window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Product-Reset"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unix timestamp (",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["milliseconds"]},") when the oldest counted request falls out of the product window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Subscription-Limit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum requests allowed in the current practice window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Subscription-Remaining"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Requests remaining in the current practice window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Subscription-Reset"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unix timestamp (",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["milliseconds"]},") when the oldest counted request falls out of the practice window."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Monitor ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["both"]}," product and subscription headers. An integration that spreads"," ","load across many practices can exhaust the product quota even when each"," ","individual practice still has headroom."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"spike-arrest-headers","__idx":6},"children":["Spike arrest headers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When spike arrest blocks a request, the response uses a simpler header set:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Header"},"children":["Header"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Limit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["10"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum requests per second for this product and practice."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Remaining"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No capacity left in the current one‑second window."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Reset"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Seconds to wait before retrying."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"when-a-limit-is-exceeded","__idx":7},"children":["When a limit is exceeded"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All rate‑limit failures return ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429 Too Many Requests"]}]}," with a"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/pages/errors"},"children":["Problem Details"]}," body:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"https://api.dentally.co/problems/too-many-requests\",\n  \"title\": \"Too Many Requests\",\n  \"status\": 429,\n  \"detail\": \"The request exceeded the configured rate limit.\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["spike arrest"]}," is the cause, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["detail"]}," is instead:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"The request exceeded the configured spike arrest limit.\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Branch on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}," and inspect ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["detail"]}," (or the response headers above) to"," ","decide how long to wait before retrying."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"handling-guidance","__idx":8},"children":["Handling guidance"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Smooth your traffic."]}," Spread requests over time instead of firing large"," ","parallel batches. Spike arrest is the first limit you are likely to hit if you"," ","burst too aggressively."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Respect response headers."]}," Track ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-*-Remaining"]}," and back off as"," ","counts approach zero."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retry with backoff."]}," On ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429"]},", wait before retrying:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Spike arrest: wait at least ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-Reset"]}," seconds (typically ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1"]},")."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Hourly quota: wait until the relevant ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RateLimit-*-Reset"]}," timestamp has"," ","passed."," ","Add jitter so many clients do not retry in lockstep."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Reuse access tokens."]}," Requesting a new token on every API call is"," ","unnecessary and may itself hit limits. See"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/pages/authentication"},"children":["Authentication"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Paginate efficiently."]}," Use the largest ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["limit"]}," your client can process to"," ","reduce round trips. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/pages/pagination"},"children":["Pagination"]},"."]}]}]},"headings":[{"value":"Rate limiting","id":"rate-limiting","depth":1},{"value":"How limits are configured","id":"how-limits-are-configured","depth":2},{"value":"Spike arrest","id":"spike-arrest","depth":2},{"value":"Hourly quotas","id":"hourly-quotas","depth":2},{"value":"Response headers","id":"response-headers","depth":2},{"value":"Hourly quota headers","id":"hourly-quota-headers","depth":3},{"value":"Spike arrest headers","id":"spike-arrest-headers","depth":3},{"value":"When a limit is exceeded","id":"when-a-limit-is-exceeded","depth":2},{"value":"Handling guidance","id":"handling-guidance","depth":2}],"frontmatter":{"seo":{"title":"Rate limiting"}},"lastModified":"2026-09-16T02:03:47.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/pages/rate-limiting","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}