Rate limits
The Shepherd API enforces two rate limits per integration key:
| Window | Limit |
|---|---|
| 1 hour | 12,000 requests |
| 1 minute | 800 requests |
Both apply simultaneously: a burst of 800 in a minute is fine, but you cannot sustain it past the hourly cap. Limits are tracked per integration key, not per clinic, so a multi-clinic integration sharing one key shares the budget.
What 429 looks like
Section titled “What 429 looks like”When you exceed either limit, the API responds with:
HTTP/1.1 429 Too Many RequestsContent-Type: text/plain
Rate limit exceededAs with other errors, the body is JSON: here, a bare string carrying the message. See Errors.
There is no documented Retry-After header to lean on; back off and try again on your own schedule.
Querying current usage
Section titled “Querying current usage”You can ask the API where you stand at any time:
POST /pav2/open-api-rate-limitsThe response tells you how many calls have been consumed in the current minute and hour windows, so you can throttle proactively rather than waiting for a 429.
curl -X POST https://open-api.shepherd.vet/pav2/open-api-rate-limits \ -H "Content-Type: application/json" \ -H "X-Integration-Public-Key: $SHEPHERD_PUBLIC_KEY" \ -H "X-Integration-Private-Key: $SHEPHERD_PRIVATE_KEY" \ -H "X-Clinic-Id: $SHEPHERD_CLINIC_ID" \ -d '{}'import os, requests
resp = requests.post( "https://open-api.shepherd.vet/pav2/open-api-rate-limits", headers={ "Content-Type": "application/json", "X-Integration-Public-Key": os.environ["SHEPHERD_PUBLIC_KEY"], "X-Integration-Private-Key": os.environ["SHEPHERD_PRIVATE_KEY"], "X-Clinic-Id": os.environ["SHEPHERD_CLINIC_ID"], }, json={}, timeout=30,)resp.raise_for_status()print(resp.json())using System.Net.Http.Json;
using var http = new HttpClient();http.DefaultRequestHeaders.Add("X-Integration-Public-Key", publicKey);http.DefaultRequestHeaders.Add("X-Integration-Private-Key", privateKey);http.DefaultRequestHeaders.Add("X-Clinic-Id", clinicId);
var body = JsonContent.Create(new { });var resp = await http.PostAsync( "https://open-api.shepherd.vet/pav2/open-api-rate-limits", body);resp.EnsureSuccessStatusCode();Console.WriteLine(await resp.Content.ReadAsStringAsync());<?php$ch = curl_init('https://open-api.shepherd.vet/pav2/open-api-rate-limits');curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-Integration-Public-Key: ' . getenv('SHEPHERD_PUBLIC_KEY'), 'X-Integration-Private-Key: ' . getenv('SHEPHERD_PRIVATE_KEY'), 'X-Clinic-Id: ' . getenv('SHEPHERD_CLINIC_ID'), ], CURLOPT_POSTFIELDS => json_encode((object)[]),]);$response = curl_exec($ch);curl_close($ch);echo $response;const resp = await fetch('https://open-api.shepherd.vet/pav2/open-api-rate-limits', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Integration-Public-Key': process.env.SHEPHERD_PUBLIC_KEY, 'X-Integration-Private-Key': process.env.SHEPHERD_PRIVATE_KEY, 'X-Clinic-Id': process.env.SHEPHERD_CLINIC_ID, }, body: JSON.stringify({}),});if (!resp.ok) throw new Error(`Shepherd ${resp.status}: ${await resp.text()}`);console.log(await resp.json());Backoff guidance
Section titled “Backoff guidance”When you receive a 429 or a transient 5xx, retry with exponential backoff and jitter. A pattern that works well in production:
- On the first retry, wait
1s + random(0, 1s). - On each subsequent retry, double the base: 2s, 4s, 8s, 16s, capped at 60s.
- Add a random jitter of up to 1 second to every wait, so concurrent workers do not synchronize.
- Give up after 5 or 6 attempts and surface the error to your caller.
Other practical tips:
- Page in larger chunks. Use
rppup to 1000 where possible; one request returning 1000 records is far cheaper than 100 requests returning 10. - Cache lookups. Reference data (species, breeds, phone types) changes rarely; cache it instead of re-embedding on every request.
- Schedule batch jobs off-peak. Bulk syncs at 03:00 clinic-local time are unlikely to collide with interactive traffic.
- Avoid retry storms. If a backfill fails halfway through, do not immediately re-run the whole window; resume from the last successful page.
Need a higher limit
Section titled “Need a higher limit”If your integration legitimately needs more headroom than 12,000 per hour, talk to your Shepherd account manager. Larger limits are negotiated per integration rather than self-service.