Fax API: send a fax with one POST request
A simple REST fax API for your product, back office or workflow. Post a PDF, get a fax ID back, and follow delivery with signed webhooks. Pay per page, no subscription.
curl https://sendfax.me/api/v1/faxes \
-H "Authorization: Bearer sfx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+15551234567",
"file": "'$(base64 < contract.pdf | tr -d '\n')'",
"filename": "contract.pdf"
}'
# → 201 Created
# { "id": "fax_3f9a…", "status": "queued",
# "pages": 3, "cost": 30, … }What the fax API does
Every feature below works today, on the endpoints documented further down this page.
Send a fax in one request
Send a base64 PDF (up to 15 MB) and an E.164 fax number to POST /api/v1/faxes. We count the pages and work out the cost for you.
Signed status webhooks
Register an https endpoint to receive fax.queued, fax.sending, fax.delivered, fax.failed and fax.received events, signed per the Standard Webhooks spec. Each delivery is tried up to 3 times.
Clear delivery statuses
Every fax moves from queued to sending, then to delivered or failed. Poll GET /api/v1/faxes/{id}, or let webhooks tell you.
Sent and received faxes
GET /api/v1/faxes lists your faxes newest first, with cursor paging and a sent or received filter. Faxes sent to your fax number show up there too.
Balance check
GET /api/v1/account returns your account email and credit balance in cents, handy for low-balance alerts in your own code.
Refunds on failure
Failed faxes are refunded automatically, and a fax the carrier rejects at dispatch is never charged. Temporary failures get one free automatic retry.
Simple rate limit
Up to 60 requests per minute per API key. Going over returns a clear 429 rate_limited error.
Keys stored hashed
API keys are shown once when you create them and stored only as SHA-256 hashes. Revoke a key from your dashboard at any time.
Copy-paste code samples
Send a fax from curl, Node.js, Python or PHP. Set SENDFAX_API_KEY to a key from your dashboard, then change the fax number and file name.
# Build the JSON body with the PDF as base64, then send it
printf '{"to":"+15551234567","filename":"contract.pdf","file":"%s"}' \
"$(base64 < contract.pdf | tr -d '\n')" > fax.json
curl https://sendfax.me/api/v1/faxes \
-H "Authorization: Bearer $SENDFAX_API_KEY" \
-H "Content-Type: application/json" \
-d @fax.jsonA successful request returns 201 Created with the fax object:
// 201 Created
{
"id": "fax_3f9a1c2b7d4e8f60a1b2",
"direction": "sent",
"to": "+15551234567",
"from": "+15550001234", // our sending fax number
"pages": 3,
"cost": 30, // cents charged
"status": "queued",
"filename": "contract.pdf",
"created_at": "2026-07-24T10:30:00.000Z",
"updated_at": "2026-07-24T10:30:00.000Z"
}API pricing
One credit balance, shared with your web account. Faxes are charged per page at the standard destination rate (10¢/page to the US) — API credit packs include bonus credit that brings your effective price down to 4¢/page. Credit never expires, and failed faxes are refunded automatically.
Fax API documentation
Authentication, endpoints, webhooks and error codes.
Authentication
Create a key in Dashboard → API and pass it as a Bearer token. Keys are shown once and stored hashed — treat them like passwords. Requests are limited to 60 per minute per key.
Authorization: Bearer sfx_live_YOUR_KEYAPI reference
Send a fax. The PDF is base64-encoded in the request body (max 15 MB). Cost is computed server-side from the page count and destination rate, and charged to your balance. Returns 201 with the fax object.
{
"to": "+15551234567", // E.164 fax number, required
"file": "<base64 PDF>", // required
"filename": "contract.pdf" // optional
}// 201 Created
{
"id": "fax_3f9a1c2b7d4e8f60a1b2",
"direction": "sent",
"to": "+15551234567",
"from": "+15550001234", // our sending fax number
"pages": 3,
"cost": 30, // cents charged
"status": "queued",
"filename": "contract.pdf",
"created_at": "2026-07-24T10:30:00.000Z",
"updated_at": "2026-07-24T10:30:00.000Z"
}Fetch one fax and its current status: queued → sending → delivered or failed (failed faxes are refunded and retried once automatically). Poll this, or use webhooks below.
List faxes, newest first. Query params: limit (1–100, default 25), before (ISO timestamp cursor), direction (sent or received — inbound faxes to your fax number appear here too).
{ "data": [ { "id": "fax_…", … } ], "has_more": false }Your account email and current credit balance in cents.
{ "email": "you@company.com", "credit_balance": 4550 }Webhooks
Register an https endpoint in the dashboard and we'll POST every fax lifecycle event to it: fax.queued, fax.sending, fax.delivered, fax.failed and fax.received (inbound). Deliveries are retried up to 3 times and logged in your dashboard.
// Event payload
{
"id": "evt_9f8e7d6c5b4a",
"type": "fax.delivered",
"created_at": "2026-07-24T10:31:40.000Z",
"data": {
"fax_id": "fax_ab12cd34ef56",
"direction": "sent",
"to": "+15551234567",
"pages": 3,
"cost": 30,
"status": "delivered"
}
}// Verify the signature (Standard Webhooks spec —
// works with any svix-compatible library)
import { Webhook } from "svix";
const wh = new Webhook(process.env.SENDFAX_WEBHOOK_SECRET);
const event = wh.verify(rawBody, {
"webhook-id": req.headers["webhook-id"],
"webhook-timestamp": req.headers["webhook-timestamp"],
"webhook-signature": req.headers["webhook-signature"],
});Errors
Errors return a JSON body: { "error": { "code", "message" } }
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, invalid, or revoked API key |
| 402 | insufficient_credit | Balance too low for this fax — top up |
| 400 | invalid_json | Request body is not valid JSON |
| 400 | invalid_number | `to` is not a valid E.164 number |
| 400 | blocked_destination | Satellite and premium-rate numbers are not supported |
| 400 | unsupported_destination | We do not deliver to that country yet |
| 400 | missing_file | `file` (base64 PDF) is missing |
| 400 | invalid_file | File is not a readable PDF (max 15 MB) |
| 404 | not_found | No fax with that id |
| 429 | rate_limited | More than 60 requests in a minute |
| 502 | carrier_rejected | Carrier refused the fax — you were not charged |
Ship your fax integration today
Create a key, grab a credit pack, and send your first API fax in minutes.
Get your API key →