Skip to content
</> Developer API

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.

Send your first fax
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.json

A 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.

Starter
$10
$15 of credit — ~150 US pages
≈ 6.7¢ per US page
Growth
$50
$100 of credit — ~1,000 US pages
≈ 5.0¢ per US page
Scale
$200
$500 of credit — ~5,000 US pages
≈ 4.0¢ per US page

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_KEY

API reference

POST/api/v1/faxes

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"
}
GET/api/v1/faxes/{id}

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.

GET/api/v1/faxes

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 }
GET/api/v1/account

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" } }

StatusCodeMeaning
401unauthorizedMissing, invalid, or revoked API key
402insufficient_creditBalance too low for this fax — top up
400invalid_jsonRequest body is not valid JSON
400invalid_number`to` is not a valid E.164 number
400blocked_destinationSatellite and premium-rate numbers are not supported
400unsupported_destinationWe do not deliver to that country yet
400missing_file`file` (base64 PDF) is missing
400invalid_fileFile is not a readable PDF (max 15 MB)
404not_foundNo fax with that id
429rate_limitedMore than 60 requests in a minute
502carrier_rejectedCarrier 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 →