Quick answer: Clio's API allows 50 requests per minute per access token at peak hours. A sustained batch trips HTTP 429 with a Retry-After header, and backoff handles that. The damaging failures are quiet: reading only the first page of a list, or filtering folders on a parent type Clio reports differently than you'd expect. Both return HTTP 200 with records missing. The 50 per minute cap next to MyCase's unpublished limit: Clio vs MyCase, what each API allows.
A common job for a growing law firm: a custom field that should hold a clean, court-formatted street address actually holds it five different ways, because intake captured whatever the client typed. Multiply that by a couple of hundred custom fields and a few thousand matters, and "just fix the data" turns into a batch-processing problem.
The instinct is to write a quick script that loops over every matter and PATCHes the fix. That script will work for the first dozen records and then start getting throttled. We hit this ourselves building our Clio MCP server (surveyed against the alternatives in the connector comparison), and the same gotchas show up whether you're normalizing data, migrating off another system, or syncing Clio with something else.
This post is the field guide we wish we'd had. It's specific to the Clio Manage API, but the batch-design principles apply to MyCase, PracticePanther, or any practice-management API with a tight rate limit. Since the first version went up, a firm ran bulk work through the connector against several hundred production matters and sent us a written report of what broke. That report is in here now, and it changed the emphasis: the 429 turned out to be the easy part.
What is the Clio API rate limit?
Clio's developer documentation puts the default at 50 requests per minute per access token during peak hours. Off-peak the limit goes up, by an amount that varies per region. The first version of this post quoted roughly 3 requests per second, a figure that floats around older integration guides. The number the throttle enforces when a firm is busiest is 50 a minute, so that's the one to design around.
That number sets the shape of everything. At 50 requests a minute, a job that has to read and write a few thousand matters does not finish in seconds. It runs for an hour or more, because each matter needs a read (to fetch current values) and a write (to PATCH the fix). So the batch isn't a quick script you babysit. It's a long-running process you design to survive interruptions.
Clio returns the standard rate-limit headers on every response. Read them instead of guessing:
- X-RateLimit-Limit and X-RateLimit-Remaining tell you how much budget is left in the current 60-second window.
- X-RateLimit-Reset is a unix timestamp for when that window ends and the budget refills.
- Retry-After on an HTTP 429 tells you how long to wait before the next call. HTTP allows two forms for it, a number of seconds or an HTTP date, so parse for both: running parseInt over a date gives NaN, and a NaN delay is not a delay.
The mistake is hardcoding a fixed sleep between calls and hoping. Honor the headers, throttle to stay comfortably under the limit, and treat a 429 as a normal event you recover from cleanly, not an error that kills the run.
Quotable rule of thumb: at 50 requests a minute, every 1,000 matters you have to read-then-write is 2,000 requests, or about 40 minutes of pure API time before you add a single second of your own processing. Budget the job in minutes-per-thousand, then design it to be resumable so the runtime doesn't matter.
What happens under sustained load: a production report
In July 2026 a firm running our Clio MCP server against a production Clio Manage account sent us a written report. Several hundred matters, bulk document-organization work, one target folder to create in every matter that didn't already have it. The person driving it was a lawyer, not a programmer; the patches he needed were written with Claude and ran in a local fork. Three things came back, and only one of them announced itself.
The one that shouted was the rate limit. Sustained batch operations reliably tripped HTTP 429. Clio returned a Retry-After, and the batch didn't go through cleanly until every call was wrapped in throttling and backoff. Annoying, but visible: the run stops and the log tells you why.
The other two never raised an error. Their routine listed a matter's folders and kept the ones whose parent type was "Matter", on the reasonable assumption that a matter's folders hang off the matter. In Clio, where a matter's document root is itself represented as a folder, the folders under it report a parent of type "Folder". The filter meant to find the target folder excluded exactly the folders it was looking for. The dry run reported that nearly every matter was missing the folder. The folders were there. Had the write pass run against that result, it would have created a duplicate folder in almost every matter in the book.
Underneath that sat a second, independent problem: the check read only the first page of folder results. Matters with many folders came back short even where the parent-type filter was right, and detection couldn't be trusted until both were fixed. Writes have a mirror of the typing issue. The parent reference on a folder-create call has to carry the type of the actual parent, Matter or Folder, and when it doesn't, Clio rejects it with a validation error generic enough that tracing it back took trial and error.
What they ended up with: drop the parent-type filter, match on folder name through the endpoint's server-side query parameter, walk the pagination to the end, throttle writes, honor Retry-After. The dry run is the reason there was nothing to clean up afterwards.
Clio API failure modes: which ones tell you
| Failure | What you see | What it costs |
|---|---|---|
| HTTP 429 under sustained load | An error and a Retry-After header | Time. Wait out the header and retry. |
| First page only on a cursor-paginated list | HTTP 200, a shorter list | Every "does this already exist?" check undercounts on big matters |
| Folder filter on parent type "Matter" | HTTP 200, an empty or short list | A write pass creates duplicates across the whole book |
| Folder create with the wrong parent type | A generic validation error | Hours of trial and error to trace |
The same first-page class of bug had been reported against our MyCase connector's task listing a few days earlier by a different firm, and was fixed in that connector's 1.2.0 release candidate. Any cursor-paginated API does this when the caller stops after page one. Clio just has more places to do it.
Do lawyers hit the Clio rate limit, or only scripts?
Both. On a call in July 2026, an attorney at a nine-attorney Oregon firm brought up Clio's rate limits before we did. He'd been using Claude against Clio for about ten months and had hit the ceiling himself, from a chat window, with no batch script anywhere. An assistant that answers one question by listing matters and then listing tasks or documents per matter can spend the whole 50-request budget on that one question, and from the lawyer's chair that reads as the tool stalling.
The comparison behind his complaint is worth stating. Clio documents 50 requests per minute per access token at peak. Lawmatics, the intake CRM the same firm runs next to Clio at about $1,800 a month, documents 150 requests per minute per firm, three times Clio's figure. Neither number is generous, and the gap shows up as soon as an assistant fans one question out into a dozen calls.
It's also why a bulk cleanup of years of messy Clio data needs a throttled, resumable runner. At 50 requests a minute nobody pushes it through by hand, and a firm that has used Clio for years without data-entry rules (the firm above described its own data as a mess, which is why it was creating folders in bulk in the first place) has thousands of records to touch.
How does the Clio MCP connector handle 429s and pagination today?
Checked against the code on the main branch at 2.2.0-beta.1 for this update. Worth pairing with what that connector writes to its audit log, since bulk work multiplies whatever a log records. The default install on npm is still 2.0.1; everything described below as shipped is on the beta tag.
Rate limits. Every request goes through one HTTP function, clioFetch. On a 429 it reads Retry-After and sleeps that many seconds; if the header is missing it falls back to 1, 2, then 4 seconds. Three retries, then it fails with an explicit "rate limit exceeded after 3 retries" error rather than handing back a partial result. It also logs a warning whenever X-RateLimit-Remaining drops below 5. It doesn't throttle ahead of time, so a batch that fires as fast as it can will still see 429s and lean on the retries, which is why the firm above added throttling on top. The report's ask, backoff that keeps waiting for as long as Clio keeps saying wait, shipped in 2.1.0: up to six attempts with jittered exponential delay, a cap on any single wait and a budget on the total, and a proactive pause once X-RateLimit-Remaining falls to three, so a long batch slows down before Clio has to refuse it. That release also fixed a quieter bug in the same function. Retry-After is allowed to be either a number of seconds or an HTTP date, and the old code ran parseInt over it: a date parsed to NaN, setTimeout(NaN) fires immediately, and the retry meant to wait became a burst at an API that had just asked for a pause.
Pagination. search_contacts and list_documents return a paginated envelope: total_count, has_more, and a next_page_token you pass back for the next page, pulled from Clio's paging.next URL. list_matters takes a limit of up to 200 and returns that one page; there's no cursor on it yet. Walking a list endpoint to the end inside the connector shipped in 2.1.0 as clioGetAllPages, used where a short read would be a wrong answer rather than a short one: existence checks, dedup, and anything that counts. It throws if it runs past its page cap instead of returning what it has, because a silent truncation is the failure this whole post is about. Folder listing, existence checks and creation shipped alongside it, with the parent typing handled inside the connector.
Until those land, the safe pattern for anything that checks whether something already exists is the one the firm used: query by name server-side, and follow has_more until it's false.
Why do Clio custom-field writes land on the wrong value?
This is the single most expensive gotcha in the Clio API, and it costs nothing at compile time. It costs you when you discover, after the batch ran, that the writes landed on the wrong values.
Reading custom fields is straightforward. You request them on the matter:
GET /matters/{id}?fields=custom_field_values{id,value,field_name,custom_field{id}}
Writing them back is where people get burned. You PATCH the matter with a nested custom_field_values array:
PATCH /matters/{id}
{
"data": {
"custom_field_values": [
{ "id": 987654, "value": "123 Main St, Suite 400" }
]
}
}
The trap: the id inside custom_field_values is the value-instance id, not the field-definition id. The value-instance id is the id of that field's value on that specific matter. It's different on every matter. The field-definition id (the thing you see as custom_field:{id}) is the same everywhere, which is exactly why it's tempting to reuse, and exactly why reusing it is wrong.
Send the definition id where the value-instance id belongs and the write does not error in the way you'd hope. It goes somewhere you didn't intend. So the correct sequence for normalization is always read-first: GET the matter, pull the value-instance id for the field you're fixing, then PATCH using that id. You cannot batch the writes blind from a list of field definitions. Every write is paired with a read.
That pairing is also why the rate limit bites harder than people expect. You don't get to do one request per matter. You do two.
Which Clio custom fields can you actually normalize?
Not every Clio custom field accepts an arbitrary cleaned-up string. The field type constrains what you can write, and a normalization engine that ignores types will fail on the first picklist it hits.
Clio custom field types and their write constraints
| Field type | What you can safely write |
|---|---|
| Free text | Anything. Normalizes cleanly. This is where most of your wins are. |
| Picklist | Only predefined options (each up to ~55 chars). You must map a messy value onto an existing option, not invent one. |
| Currency | Rejects decimals in the value. Plan your formatting accordingly. |
| Date | Needs a valid date. A "cleaned" string that isn't a real date fails. |
So step one of any real normalization job is not writing code. It's an inventory: pull every custom-field definition, group by type, and decide the rule per type. Free-text fields are where an LLM like Claude earns its keep, reading the messy entry and emitting your house format. Picklists are a mapping problem, not a generation problem. Currency and date fields are validation problems. Promise "we'll normalize every field" before you've done this inventory and you'll be wrong about a meaningful slice of them.
One more boundary worth stating plainly: the OAuth user running the batch needs write access to every matter it touches. If your token belongs to someone whose permissions don't cover the whole book of business, the batch will skip or fail on the matters they can't reach.
How do you make a Clio batch job resumable?
Here's the part the quick script always skips. A job that runs for an hour against a throttled API will get interrupted. Your laptop sleeps, the token expires, a 500 comes back from Clio, the network blips. The question isn't whether it stops. It's whether stopping costs you the whole run.
The design that survives all of that has five properties:
1. Paginate explicitly and checkpoint your position
Clio paginates at up to 200 records per page and hands you the next page as a paging.next URL. Follow it until it's absent; a check that stops at page one is the first silent failure in the production report above. Walk the pages in order and persist the last successfully processed cursor or matter id. If the job dies on page 47, it restarts on page 47, not page 1. The checkpoint lives outside the process: a small state file or table, written after each page commits.
2. Make every write idempotent
If you re-run a page after a crash, re-processing an already-fixed matter must be a no-op. The cheapest way: before writing, compare the current value to the target value and skip if they already match. Normalization is naturally idempotent when you do this, because a clean value normalizes to itself.
3. Treat a 429 as flow control
On a 429, sleep for the Retry-After duration and retry the same request, handling both forms the header is allowed to take and falling back to jittered backoff when it is neither. On a 5xx, use exponential backoff with a cap, then retry. Only after repeated failures do you log the matter to a dead-letter list and move on. The run should never die because one matter misbehaved. The production report above is the practical version of this rule: the batch went through once every call had a throttle in front of it and a Retry-After wait behind it.
4. Dry-run and preview before you write a single byte
The feature that earns trust is a preview mode. Run the whole job read-only first, produce a per-matter diff of "current value to proposed value," and let a human eyeball it. Nobody approves an unattended write across thousands of privileged matters on faith. They approve it after seeing the diff. In the production report above, the dry run said nearly every matter needed a new folder, which was wrong on its face, and that's where the investigation started.
5. Keep a per-matter rollback record
Before each write, log the old value alongside the new one. If the normalization rule turns out wrong on field 12, you can replay the log in reverse and restore it. Without this, "undo" means manual cleanup, which is the exact chore you were trying to automate away.
The honest tradeoff: all five of these make the batch slower to build than the naive loop. They also make it the difference between a tool a law firm will run against live client data and a script nobody trusts to touch their matters. For privileged data, the audit trail and the rollback log aren't nice-to-haves. Our connector keeps an append-only audit log for exactly this reason, framed to support the kind of recordkeeping ABA Opinion 512 and equivalent guidance expect.
What other Clio API behaviors change the design?
Document upload is a two-step presigned-S3 flow, not a multipart POST
If your batch also writes documents back into matters (say, a generated summary or a corrected form), don't expect a single upload endpoint. Clio uses a three-call dance: POST to /documents to register the file and get a put_url, PUT the raw bytes directly to that S3 URL, then PATCH the document with fully_uploaded: true to commit it. Skip the final PATCH and the document exists but stays invisible. We implement this in the upload_document tool of the connector so callers don't have to hand-roll the S3 step.
There is no document webhook, so you poll or trigger on matter updates
If your goal is to react to new documents (not just bulk-fix existing data), know that Clio has no document webhook. Webhooks exist for activity, bill, calendar_entry, communication, contact, matter, and task, and they auto-expire after 3 days by default (31 max), so a long-lived integration has to renew them on a schedule. To detect new documents you either poll on an interval or, more cleverly, trigger on the matter updated webhook when a stage change is the thing that causes document generation. Clio Draft's document automation is UI-only, with no API to trigger it, so the "generate then fill" pattern has to be assembled from the pieces that do have API surface.
Where the rate limit meets data residency
One reason batch design matters more for legal data than for a typical SaaS migration: if an LLM is doing the normalization on free-text fields, you're sending field values to a model provider, and for a Canadian family-law practice under PIPEDA and Law Society guidance, that crosses a line you have to account for.
A few facts worth knowing before you architect this. Clio runs a Canadian region (ca.app.clio.com), so your practice-management data can stay in Canada, and your batch must call the CA base URL if the account lives there. Anthropic, by contrast, has no Canadian data region today: Claude processes in the US. The practical mitigation is a zero-data-retention arrangement on the Claude API (note that ZDR is available org-level on the API, not on the Team chat plan), pinned to US inference, with the cross-border step documented for your confidentiality obligations. The slow pace forced by the 50-a-minute limit is almost helpful here: nothing about this pipeline needs to be fast, so "pull, normalize, write back, retain nothing" is an easy posture to hold.
This is the kind of decision we'd rather you make on purpose than discover after the fact. There isn't one correct architecture. A purely deterministic normalizer that never touches an LLM avoids the cross-border question entirely, at the cost of handling fewer messy cases. An LLM-assisted normalizer handles the long tail of human-entered chaos but adds a data-handling step you have to be honest about. The right call depends on your data and your obligations, not on which is more impressive.
Summary
- Design around 50 requests a minute per access token (more off-peak). Read
X-RateLimit-*andRetry-After; never hardcode a sleep. - The failures that cost money are silent: first-page-only reads and a folder filter on parent type "Matter" both return HTTP 200 with records missing. Walk every list to the end and dry-run against a count you trust.
- Custom-field writes use the value-instance id, not the field-definition id. Always read before you write.
- Inventory fields by type first. Free text is easy; picklist, currency, and date have format rules you map before promising anything.
- Make the batch resumable, idempotent, and reversible, with a dry-run preview, because it will get interrupted and it's touching privileged data.
- Documents upload via presigned S3 (POST, PUT, PATCH
fully_uploaded), and there's no document webhook, so poll or trigger onmatter updated. - If an LLM normalizes free text, account for data residency: Clio has a Canadian region, Anthropic doesn't, so use ZDR + US-pinned inference and document the cross-border step.
- The open-source connector retries a 429 with Retry-After in both its permitted forms, backs off with jitter under a total wait budget, and slows down before the limit rather than after. Reads where a partial answer would be wrong walk every page to the end.
Frequently asked questions
What is the Clio API rate limit?
Clio documents a default of 50 requests per minute per access token during peak hours, with higher limits off-peak that vary by region. Treat it as a ceiling: read the X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on every response, throttle below the limit, and on an HTTP 429 wait for the period in Retry-After, which is either a number of seconds or an HTTP date. At 50 a minute a job touching thousands of records runs for an hour or more, so the batch must be designed to take its time.
How do you update a Clio custom field via the API?
PATCH the matter with a nested custom_field_values array. The id inside that array is the value-instance id (the id of that field's value on that specific matter), not the field-definition id (custom_field:{id}). Read the value-instance id from GET matter?fields=custom_field_values{...} first, then PATCH with it. Sending the definition id silently writes to the wrong place.
How do you upload a document to Clio via the API?
Clio uses a two-step presigned-S3 upload. POST to /documents to get a put_url, PUT the file bytes directly to that S3 URL, then PATCH the document with fully_uploaded set to true. It's not a single multipart POST, and skipping the final PATCH leaves the document invisible.
Does Clio have a webhook for new documents?
No. There's no document webhook. Webhooks exist for activity, bill, calendar_entry, communication, contact, matter, and task, and they auto-expire (3 days default, 31 max) so you must renew them. To react to new documents, poll on an interval or trigger on the matter updated webhook when a stage change causes document generation.
Why does a Clio API batch return incomplete results without an error?
Two common causes, both HTTP 200. List endpoints such as matters and folders are cursor-paginated, and a caller that reads only the first page silently undercounts. And a folder's parent carries a type: where a matter's document root is itself a folder, the folders under it report parent type Folder rather than Matter, so a filter on parent type Matter excludes them. A production firm's dry run reported nearly every matter missing a folder that was actually present because of exactly these two issues. Follow paging.next to the end and match folders by name with the server-side query parameter.
Is the Clio API rate limit lower than other legal software APIs?
Clio documents 50 requests per minute per access token at peak hours, higher off-peak by region. Lawmatics documents 150 requests per minute per firm, three times Clio's figure. An AI assistant that fans one question out into many list calls hits the Clio ceiling first; an attorney at a nine-attorney firm told us he had hit it himself from a chat window, with no batch script involved.
Does the Clio MCP connector handle rate limits and pagination?
Yes, as of 2.1.0. Retry-After is parsed in both the forms HTTP allows, seconds and a date, and backoff is jittered with a cap on the single wait and on the total. clioGetAllPages walks a list endpoint to the end for reads where a partial answer would be a wrong answer, and throws rather than truncating silently if it runs past its page cap. Folder listing, existence checks and creation shipped in the same release. This is the default install as of 2.2.0. It has not been exercised against a live Clio account on our side, which the release notes say plainly.
Want a second set of eyes on your Clio integration?
We build privilege-aware Clio and MyCase integrations for law firms, and we open-sourced the connectors that handle the upload flow, the audit logging, and the custom-field handling described above. If you're staring at a few thousand messy records, or a workflow you keep doing by hand, we'll look at your setup and give you an honest read on what's a config change, what's net-new connector work, and what the rate limit means for your timeline.
Book a 30-minute technical review →
Or read more from our legal AI integration practice:
- How to Build a Claude MCP Server for Law Firms
- Clio MCP Connectors Compared
- Legal Tech Software Development
Rate limits bite hardest during a bulk backfill, which is usually someone catching up on the Clio Grow to Clio Manage handoff or running a nightly three-way reconciliation check. Plan the backoff before either.
You are paying enterprise prices for a 50-requests-per-minute API. If that stings, see what the subscription really totals over three years.