XRPpdf API
Programmatic PDF OCR for production workloads. Authenticate with an API key, submit PDFs, receive searchable PDFs back on a webhook or via polling. Paid in XRP — no credit cards, no chargebacks, no subscriptions.
1. Quick start
- Link your XRP wallet on the homepage and send XRP to your account's deposit address + destination tag. Your credits update automatically.
- On the dashboard, click Create API key. Save the key — it's shown only once.
- Make your first request:
curl -X POST https://xrppdf.com/api/v1/ocr \
-H "X-API-Key: xrpocr_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-F "[email protected]"
Response (HTTP 200):
{
"job_id": "aBcD1234xyZ7",
"pages_charged": 3,
"status_url": "/api/job/aBcD1234xyZ7/status"
}
2. Authentication
Every /api/v1/* endpoint requires an API key. You can pass it either way:
Authorization: Bearer xrpocr_live_...
# or
X-API-Key: xrpocr_live_...
Error responses
| Code | Meaning |
|---|---|
| 400 | Malformed request (bad PDF, invalid params). |
| 401 | Missing / invalid / revoked API key. |
| 402 | Insufficient credits. Top up with XRP to continue. |
| 403 | Not allowed for this auth method (e.g. API key trying to manage keys). |
| 429 | Rate-limited OR concurrency limit reached. |
| 502 | Upstream OCR engine unavailable (credits are refunded automatically). |
3. Endpoints
POST /api/v1/ocr
Submit a PDF for OCR. Multipart form upload, field name file.
| Header | Required | Purpose |
|---|---|---|
| Authorization / X-API-Key | yes | API key |
| Idempotency-Key | recommended | Client-generated UUID (8–100 chars). Repeat requests with the same key replay the first response byte-for-byte for 24 h — safe to retry on network errors without double-charging. |
Success body:
{
"job_id": "<url-safe 12 chars>",
"pages_charged": <int>,
"status_url": "/api/job/<job_id>/status"
}
GET /api/v1/jobs/{job_id}
Poll a job's status. The response shape is stable; new fields may be added.
{
"job_id": "aBcD1234xyZ7",
"status": "complete", // queued | processing | complete | error | deleted
"total_pages": 3,
"overall_confidence": 95.4, // Tesseract mean confidence (0–100)
"processing_seconds": 7.2,
"pages_charged": 3,
"error_message": null,
"created_at": 1713400000,
"completed_at": 1713400008,
"expires_at": 1713486400, // output auto-deleted after this
"download_url": "/download/aBcD1234xyZ7" // null until status=complete
}
GET /download/{job_id}
Returns the searchable PDF. Requires the same API key that submitted the job.
Stream the body to disk. After expires_at, returns HTTP 410.
GET /api/v1/account
Shows your current balance, deposit tag, concurrency limit, and in-flight job count. Useful for pre-flight checks before a batch submission.
4. Idempotency
All POST /api/v1/ocr requests should include an
Idempotency-Key header. If your HTTP client retries after a
network error, we'll return the original response — no duplicate job, no
duplicate debit. Cached responses live for 24 hours.
curl -X POST https://xrppdf.com/api/v1/ocr \
-H "X-API-Key: xrpocr_live_..." \
-H "Idempotency-Key: batch-2026-04-17-invoice-00042" \
-F "[email protected]"
5. Webhooks
Register a webhook from the dashboard (or via API) and we'll POST a signed
payload to your URL when each job finishes — job.completed or
job.failed. Delivery retries with exponential backoff up to
5 attempts (30s, 2m, 10m, 30m, 2h). Respond with any 2xx to acknowledge.
Payload
{
"event": "job.completed",
"job_id": "aBcD1234xyZ7",
"status": "complete",
"total_pages": 3,
"overall_confidence": 95.4,
"processing_seconds": 7.2,
"pages_charged": 3,
"expires_at": 1713486400,
"error_message": null,
"timestamp": 1713400008
}
Verifying the signature
Each webhook carries two headers:
| Header | Value |
|---|---|
| X-XRPOCR-Timestamp | Unix timestamp (seconds) |
| X-XRPOCR-Signature | sha256=<hex> |
| X-XRPOCR-Job-Id | Job public ID (convenience) |
The signature is HMAC_SHA256(secret, "<timestamp>." + raw_body),
using the webhook secret shown when you registered. Reject requests whose
signature doesn't match OR whose timestamp is more than 5 minutes old to
prevent replay attacks.
# Python verification example
import hmac, hashlib
def verify(secret, timestamp, signature, raw_body):
expected = "sha256=" + hmac.new(
secret.encode(),
f"{timestamp}.".encode() + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
6. Concurrency limits
Each account has a concurrency cap — the max number of jobs that can be in
flight (queued or processing) at the same time. Exceeding it returns HTTP
429 with {"in_flight": N, "limit": N}. Defaults:
| Tier | Concurrency |
|---|---|
| Pay-as-you-go | 2 |
| Pro | 5 |
| Scale | 20 |
| Enterprise | Custom |
Higher limits are granted automatically after your first qualifying deposit (Pro bundle ≥ 8 XRP, Scale bundle ≥ 30 XRP). Need more? Contact us.
7. Python client example
import uuid, time, requests
API = "https://xrppdf.com"
KEY = "xrpocr_live_..."
def ocr(pdf_path):
r = requests.post(
f"{API}/api/v1/ocr",
headers={"X-API-Key": KEY, "Idempotency-Key": str(uuid.uuid4())},
files={"file": open(pdf_path, "rb")},
timeout=60,
)
r.raise_for_status()
return r.json()["job_id"]
def wait(job_id, timeout=900):
end = time.time() + timeout
while time.time() < end:
r = requests.get(f"{API}/api/v1/jobs/{job_id}",
headers={"X-API-Key": KEY}, timeout=15)
j = r.json()
if j["status"] == "complete":
return j
if j["status"] == "error":
raise RuntimeError(j.get("error_message"))
time.sleep(2)
raise TimeoutError(job_id)
def download(job_id, out_path):
r = requests.get(f"{API}/download/{job_id}",
headers={"X-API-Key": KEY}, stream=True, timeout=120)
r.raise_for_status()
with open(out_path, "wb") as f:
for chunk in r.iter_content(65536):
f.write(chunk)
job = ocr("invoice.pdf")
info = wait(job)
download(job, "invoice_searchable.pdf")
print(f"Done in {info['processing_seconds']}s, confidence {info['overall_confidence']}%")
8. Rate limits & pricing
- Rate limit: 120 requests per minute per IP on
/api/v1/ocr. - File size: up to 100 MB per PDF, 100 pages per job.
- Pricing: see the pricing page — volume tiers kick in automatically based on XRP deposit size.
- Retention: outputs auto-delete after 24 hours. Downloads are one-shot; store the PDF on your side.
9. Payments & refunds
All payments are in XRP. Credits are issued only on validated ledger transactions to our deposit address with your account's destination tag. Nothing is credited speculatively; the listener ignores failed, tentative, or issued-currency amounts.
Refund policy. Refunds are issued in XRP on-chain for hard processing failures only — the engine returned an error after the job started, or the output download failed. The refund is sent back to the same wallet that funded your account, at your account's blended effective per-page rate (so bulk buyers get bulk-rate refunds).
- Where it goes: the
wallet_addresson file for the account (the one you linked). - When it fires: automatically, on detected hard failure — no support ticket required.
- Delivery: within minutes of failure, from the hot wallet, with exponential-backoff retry on ledger errors.
- Dust floor: refunds below 0.1 XRP equivalent are credited to your balance instead of sent on-chain, to save you the ledger fee.
- Safety net: if the on-chain send cannot be completed after 5 attempts, the pages are credited back to your account so you're never stuck.
Soft failures (engine unreachable before your job was accepted, concurrency-limit race) don't consume service and are handled by a straight credit refund — your balance is restored immediately so you can retry.
10. Support
Problems, feature requests, or enterprise questions: [email protected]. Include your account's wallet address (never your API key).
xrpocr?
XRPpdf was originally launched as XRPOCR. API key prefixes
(xrpocr_live_) and webhook headers (X-XRPOCR-*)
are stable wire-protocol identifiers — they won't change. Treat them as
opaque strings. The public brand and domain are XRPpdf /
xrppdf.com going forward; xrpocr.com redirects here.