Loading documentation…
Loading documentation…
All PearMedica API requests require authentication via API keys. This guide covers key management, rate limiting, and security best practices.
PearMedica issues two types of API keys, distinguished by their prefix:
| Prefix | Environment | Usage |
|---|---|---|
| sk_live_ | Production | Live patient assessments. Usage is metered and billed. |
| sk_test_ | Development | Sandbox testing. Returns mock data, not billed. |
Include your API key in the Authorization header of every request:
Authorization: Bearer sk_live_your_api_key_here
Security Best Practices
sk_test_ keys for development and testingEvery API key gives you full access to the entire PearMedica platform. There are no endpoint-specific restrictions — your single key works across all endpoints.
| Endpoint | Purpose | Billing |
|---|---|---|
| POST /v1/assess | AI symptom assessment | ✦ Billable |
| POST /v1/parse | NLP symptom extraction | Free |
| GET /v1/symptoms | List symptoms catalogue | Free |
| GET /v1/risk-factors | List risk factors | Free |
| GET /v1/lab-tests | List lab tests | Free |
| GET /v1/conditions/{id} | Condition details | Free |
| GET /v1/conditions/{id}/education | Patient education | Free |
| GET /v1/search | Knowledge search | Free |
| GET /v1/rationale | Assessment reasoning | Free |
| GET /v1/usage | Usage statistics | Free |
| GET /v1/health | Health check (no auth) | Free |
| POST /v1/fhir/assess | FHIR R4 assessment | ✦ Billable |
| GET /v1/fhir/metadata | FHIR capability statement | Free |
Only POST /v1/assess and POST /v1/fhir/assess count toward your monthly assessment quota. All other endpoints are free and unlimited.
Manage your API keys from the Dashboard → API Keys page. Available actions:
Generate a new API key with a descriptive label. The full key is shown once — save it immediately.
Generates a new key and invalidates the old one. Use POST /v1/keys/rotate or the dashboard.
Permanently disables a key. All requests using it will receive 401 Unauthorized.
See per-key usage metrics: total requests, last used timestamp, and rate limit status.
All API keys have a default rate limit of 100 requests per minute using a sliding window algorithm. Rate limit headers are included in every response:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 87X-RateLimit-Reset: 1709193600
| Tier | Rate Limit | Monthly Quota |
|---|---|---|
| Pilot | 100 req/min | 200 assessments included |
| Professional | 100 req/min | 2,500 included + ₦100/additional |
| Enterprise | Custom | Custom — contact sales |
If you exceed the rate limit, you'll receive a 429 Too Many Requests response. Implement exponential backoff to handle this gracefully.
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed per window (e.g., 100). |
| X-RateLimit-Remaining | Requests remaining in the current window. |
| X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
| Retry-After | Seconds to wait before retrying (only present on 429 responses). |
async function fetchWithRetry(url, options, maxRetries = 3) {const baseDelayMs = 1000;for (let attempt = 0; attempt < maxRetries; attempt++) {const res = await fetch(url, options);if (res.status !== 429) return res;// Use server-provided Retry-After directly when available;// only fall back to exponential backoff when absent or invalid.const retryAfterRaw = parseInt(res.headers.get('Retry-After') || '', 10);const backoff = Number.isFinite(retryAfterRaw) && retryAfterRaw > 0? retryAfterRaw * 1000: baseDelayMs * Math.pow(2, attempt);await new Promise(r => setTimeout(r, backoff));}throw new Error('Rate limit exceeded after max retries');}
For billable POST endpoints, include an Idempotency-Key header to prevent duplicate processing on network retries. This is critical in Africa's unreliable network environment.
const response = await fetch('https://api.pearmedica.com/v1/assess', {method: 'POST',headers: {'Authorization': 'Bearer sk_live_your_key','Content-Type': 'application/json','Idempotency-Key': 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', // UUID},body: JSON.stringify({ patient, evidence }),});// If you retry with the same Idempotency-Key, you'll get// the cached response — no duplicate assessment or billing.
409 ConflictAvailable today: Assessment sessions already have natural idempotency via the interview_id field. Sending the same interview_id continues an existing session rather than creating a duplicate.