Rate limits
To keep the service fast for everyone, conversion requests (POST /v1/convert and POST /v1/convert/batch) are limited per account per minute, whatever the size of the files. Reading jobs, usage and keys, and GET /health, are not rate limited at the moment.
The limits
| Tier | Conversion requests per minute |
|---|---|
| Free | 20 requests |
| Higher tiers | up to 300 requests |
GET /v1/usage tells you your own limit in rate_limit_per_minute. The limit counts requests, not pages: a batch of 100 files is one request.
What happens when you exceed it
The request is refused with 429 Too Many Requests and these headers:
| Header | Meaning |
|---|---|
Retry-After |
Seconds to wait before trying again |
X-RateLimit-Limit |
Your limit per minute |
X-RateLimit-Remaining |
0 |
The X-RateLimit-* headers are sent on 429 answers only, not on every response.
How to stay within the limit
- Use batches instead of many single requests when you have many files.
- Use asynchronous mode and webhooks instead of polling a job in a tight loop.
- Wait for
Retry-Afterwhen you get a429, then retry. Do not retry immediately in a loop. - Spread bursts over time, for example with a small queue in your application.
A polite retry
The example retries after a 429 or a 503 (a provider that is busy), waits for the time the API asks for, and stops with a clear message on 402 (quota used up):
# curl retries 429 and 503 by itself, waiting for the Retry-After the API sends
curl -s --retry 3 --fail-with-body https://api.docslice.app/v1/convert \
-H "Authorization: Bearer $DOCSLICE_API_KEY" \
-F "file=@invoice.pdf" \
-F "format=json"
import json
import os
import time
import requests
api_key = os.environ["DOCSLICE_API_KEY"]
def convert(path):
for attempt in range(4):
with open(path, "rb") as handle:
response = requests.post(
"https://api.docslice.app/v1/convert",
headers={"Authorization": f"Bearer {api_key}"},
files={"file": (path, handle)},
timeout=120,
)
if response.status_code in (429, 503): # too many requests, or the provider is busy: wait and retry
time.sleep(int(response.headers.get("Retry-After", "5")))
continue
if response.status_code == 402:
raise SystemExit("The monthly page quota is used up")
response.raise_for_status()
return response.json()
raise SystemExit("The service stayed busy: try again later")
print(json.dumps(convert("invoice.pdf"), indent=2))
const fs = require("node:fs");
async function convert(path) {
for (let attempt = 0; attempt < 4; attempt++) {
const form = new FormData();
form.append("file", new Blob([fs.readFileSync(path)]), path);
const response = await fetch("https://api.docslice.app/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` },
body: form,
});
if (response.status === 429 || response.status === 503) {
// too many requests, or the provider is busy: wait and retry
await new Promise((resolve) => setTimeout(resolve, 1000 * Number(response.headers.get("Retry-After") || 5)));
continue;
}
if (response.status === 402) throw new Error("The monthly page quota is used up");
if (!response.ok) throw new Error(`HTTP ${response.status}: ${JSON.stringify(await response.json())}`);
return response.json();
}
throw new Error("The service stayed busy: try again later");
}
convert("invoice.pdf").then((data) => console.log(JSON.stringify(data, null, 2)));
<?php
function convert($path) {
for ($attempt = 0; $attempt < 4; $attempt++) {
$retryAfter = 5;
$curl = curl_init('https://api.docslice.app/v1/convert');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DOCSLICE_API_KEY')],
CURLOPT_POSTFIELDS => ['file' => new CURLFile($path)],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
CURLOPT_HEADERFUNCTION => function ($curl, $line) use (&$retryAfter) {
if (stripos($line, 'Retry-After:') === 0) {
$retryAfter = (int) trim(substr($line, 12));
}
return strlen($line);
},
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
if ($status === 429 || $status === 503) { // too many requests, or the provider is busy: wait and retry
sleep($retryAfter);
continue;
}
if ($status === 402) {
exit("The monthly page quota is used up\n");
}
if ($status >= 400) {
exit("HTTP $status: $body\n");
}
return json_decode($body, true);
}
exit("The service stayed busy: try again later\n");
}
echo json_encode(convert('invoice.pdf'), JSON_PRETTY_PRINT);
More on error handling in Errors and troubleshooting.