REST API v1.0

API Documentation

Integrate press release distribution directly into your workflow. Submit releases, sync status, retract pending ones, and retrieve reports — all via REST API.

Base URL
/api/v1
Auth
Bearer API Key
Format
JSON

Authentication

Authenticated endpoints require an API key passed via the Authorization header:

bash
curl -H "Authorization: Bearer prnow_abc123..." \
  https://prnow.io/api/v1/status/<press_release_id>

Which ID? The /status, /report, /links and /retract endpoints accept either the pressReleaseId (UUID) or the slugId — the short code shown in your dashboard (e.g. G9E1HJK). Both are returned by POST /api/v1/submit.

Generate API keys from your PRNow account, or via POST /api/v1/keys while logged in. Keys are prefixed with prnow_ and shown in full only once at creation.

Test Your API Key

Before making any other requests, verify your key is valid and see your account details with this endpoint. It returns your account email, name, company, and your spendable credit balance across all of your credit wallets.

bash
curl -H "Authorization: Bearer prnow_your_key_here" \
  https://prnow.io/api/v1/test
Response (200 OK)
{
  "success": true,
  "data": {
    "message": "API key is valid",
    "account": {
      "id": "...",
      "email": "office@profitlabs.net",
      "name": "Your Name",
      "company": "Profit Labs",
      "credits": 5,
      "maxSingleWalletCredits": 5,
      "wallets": {
        "default": 5,
        "custom": null,
        "reseller": []
      }
    }
  }
}

credits is your total across all wallets. A single submission is paid from one wallet, so check maxSingleWalletCredits (or the wallets breakdown) to know the largest order you can place right now.

401 Unauthorized means the key is invalid, revoked, or missing the Bearer prefix.

Release Statuses

The status returned by GET /api/v1/status/{id}. When a release is rejected or refunded, statusReason tells you why.

StatusMeaning
draftSaved but not submitted. Not in the review queue and not charged.
pendingSubmitted and awaiting review. The only status you can retract from.
in_progressOur editorial team has started work. Retraction is no longer possible.
publishedLive. A report becomes available.
distributedSent out to the outlets on the order.
rejectedDeclined by our editorial team. Any credits are returned as part of the same operation — see statusReason.
refundedThe order was cancelled and credits returned — usually by your own POST /retract/{id}.

rejected and refunded are not sequential. They are two independent outcomes, not consecutive stages. A rejection returns the credits as part of the same operation, so a rejected release is not followed by a separate refunded status — don't poll waiting for that change.

Status is not a refund signal. If only some outlets on an order are rejected, those credits are refunded and the order's status does not change — it stays whatever it already was. The packages array on /status/{id} is the only place that shows it — each entry carries its own state, creditsRefunded and reason. A package with no entry hasn't been actioned yet — treat absent as still pending.

Status is not a placement signal either. On a multi-outlet order one outlet can go live days before the rest. The order stays in_progress and /links/{id} stays closed until every outlet is done — but packages reports that outlet as live with its liveUrl the moment the placement is recorded, and reports an outlet our team had to step in on as held with a reason. For the sequence of what happened — placements, partial rejections, a release bounced back to the queue — call GET /api/v1/status/{id}/events.

Retract a Pending Release

Changed your mind after submitting? While a release is still pending review you can retract it and get your credits back instantly — the same action as the Retract button in your dashboard. The credits return to the wallet that paid, and the release becomes editable so you can resubmit it.

bash
curl -X POST \
  -H "Authorization: Bearer prnow_your_key_here" \
  https://prnow.io/api/v1/retract/G9E1HJK
Response (200 OK)
{
  "success": true,
  "data": {
    "id": "8f14e45f-ceea-467a-9f2b-1c2d3e4f5a6b",
    "pressReleaseId": "8f14e45f-ceea-467a-9f2b-1c2d3e4f5a6b",
    "slugId": "G9E1HJK",
    "status": "refunded",
    "creditsRefunded": 205,
    "message": "Press release retracted successfully"
  }
}

creditsRefunded is the amount credited back by this call. If part of the order was already refunded, only the remaining balance is returned.

Only pending releases can be retracted. Once our editorial team has started work, or the release is already live, retraction is no longer possible and you'll get a 400 — open a support ticket instead. A 409 means it was already retracted, and no credits were refunded by that call.

Safe Retries

A request can time out after it has already gone through. To retry POST /api/v1/submit or POST /api/v1/draft with no risk of ordering twice, send an Idempotency-Key header — a unique value you generate for each order (a UUID is ideal) — and resend the exact same request with the same key.

bash
curl -X POST https://prnow.io/api/v1/submit \
  -H "Authorization: Bearer prnow_your_key_here" \
  -H "Idempotency-Key: 8f14e45f-ceea-467a-9f2b-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d @release.json
A retry with the same key, when…Returns
The first request completedThe original response again — same pressReleaseId — with the header Idempotent-Replayed: true. Nothing is charged or created twice.
The first request is still being processed409 idempotency_request_in_progress with a Retry-After header. Wait that many seconds and retry to receive its result.
The key was already used with a different request body422 idempotency_key_reused. Use a new key for a new order.
The first request was refused (400, 402, 409…)Nothing was taken and the key is free again — fix the request and retry with the same key.
The first request failed or timed out partwayIts result if it went through, or a fresh attempt if it provably took nothing. If its outcome can't be confirmed automatically: 409 idempotency_request_interrupted — contact support with the key. You are never charged twice.

One key per order. Keys are scoped to your account and to the endpoint, and are remembered for 24 hours. Generate a fresh key for every new release — never reuse one across different releases.

No key? An identical resubmission is still refused safely: if you already have a live release with the same title and plan, POST /submit returns 409 same_plan_duplicate without charging, and existingPressRelease tells you which release it was.

List your releases

GET /api/v1/releases returns your releases newest first, drafts included — handy for reconciling what actually landed. Filter with status and since; page with limit (up to 100) and cursor.

bash
curl -H "Authorization: Bearer prnow_your_key_here" \
  "https://prnow.io/api/v1/releases?since=2026-10-01T00:00:00Z&limit=20"
Response (200 OK)
{
  "success": true,
  "data": [
    {
      "id": "8f14e45f-ceea-467a-9f2b-1c2d3e4f5a6b",
      "pressReleaseId": "8f14e45f-ceea-467a-9f2b-1c2d3e4f5a6b",
      "slugId": "G9E1HJK",
      "title": "Acme Launches Its New Widget Platform",
      "status": "pending",
      "plan": "standard",
      "creditsUsed": 49,
      "submittedVia": "api",
      "createdAt": "2026-10-14T12:00:00.000Z",
      "updatedAt": "2026-10-14T12:00:00.000Z",
      "publishedAt": null,
      "scheduledPublishDate": null,
      "scheduledTimezone": null
    }
  ],
  "pagination": { "limit": 20, "hasMore": false, "nextCursor": null }
}

When hasMore is true, pass nextCursor back as cursor to fetch the next page.

Reference Values

When submitting a press release, categories must use values from the list below, and country must use a country name from the countries list. Both lists are also available via API at GET /api/v1/categories and GET /api/v1/countries.

Content Limits

  • title — 5–200 chars, min 5 words
  • summary — max 45 words (auto-truncated)
  • content — 250–1,200 words
  • links in content — max 5
  • inline images — max 3
  • YouTube embeds — max 1
  • Google Maps embeds — max 1
  • categories — 1 to 5 from list below
  • metaTitle — max 60 chars
  • metaDescription — max 200 chars
  • metaKeywords — max 250 chars
  • imageAlt — max 125 chars (SEO)
  • imageCaption — max 300 chars
  • country — required (name or ISO-2 code); city — required

Valid Categories

GET /api/v1/categories

Pass one or more of these exact strings in the categories array. Values are case-sensitive.

Technology
Finance
Healthcare & Medicine
Business & Professional Services
Energy & Environment
Retail & Commerce
Manufacturing & Construction
Government & Law

Valid Countries

GET /api/v1/countries

Use the name value (not the code) in the country field. Example: "country": "United States"

AfghanistanAF
AlbaniaAL
AlgeriaDZ
AndorraAD
AngolaAO
ArgentinaAR
ArmeniaAM
AustraliaAU
AustriaAT
AzerbaijanAZ

Embedding Google Maps & YouTube videos

You can embed one Google Map and one YouTube video inside the content HTML. Only YouTube and Google Maps are allowed — any other iframe is removed automatically. There are two ways:

1. Shortcut (easiest) — wrap any URL in <embed>…</embed>

Paste any YouTube link (watch, share, or short) or any Google Maps link — we build the correct responsive embed for you. No need to hand-write an iframe.

<p>…your article…</p>

<embed>https://www.youtube.com/watch?v=dQw4w9WgXcQ</embed>

<embed>https://www.google.com/maps?q=Truesdale+Nursery,+NY</embed>

Accepted YouTube forms: youtube.com/watch?v=…, youtu.be/…, youtube.com/embed/…, youtube.com/shorts/…. For Maps, any google.com/maps… URL works (we append output=embed automatically). Best for partner newswires: a Google Maps place link (the one Share → Copy link opens, with /maps/place/…) or the Share → Embed a map URL — those become Google's own embed form, the shape partner newswires accept (some outlets may still show a link); a plain place name or ?q= search renders on prnow but is more likely to appear as a link on partners.

2. Or paste the official embed iframe directly

From YouTube use Share → Embed; from Google Maps use Share → Embed a map, then paste that <iframe> into content. Only youtube.com / youtube-nocookie.com and Google Maps (google.com/maps, maps.google.com) hosts are kept; everything else is stripped. When your release is distributed to partner newswires, each embed travels as the real embed in the partner's own format.

<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!…"
  width="600" height="450" style="border:0;" allowfullscreen loading="lazy"></iframe>

Tip: embeds don't count toward your 5-link limit. Kept iframe attributes: src, width, height, frameborder, allow, allowfullscreen, loading, referrerpolicy.

AI tools

Run our AI press-release tools from your own site or app with POST /api/v1/tools/{tool}. Each run costs its price in credits, taken from your wallet once the free credits are used up. A run that fails is refunded. Each run is capped at 85 seconds; a run that takes longer is refunded too, so set your client timeout above 90 seconds. GET /api/v1/tools lists each tool's inputs and, with your key, how much of this month's allowance is left.

Interactive API Explorer

Test the endpoints directly below. To use secured endpoints, click "Authorize" on the right and input your API key (e.g. prnow_xxxxx).

Error Codes

CodeMeaning
200Success
400Bad request — missing or invalid fields
401Unauthorized — invalid or missing API key
402Payment required — insufficient credits
404Not found — press release doesn't exist or not owned by you
409Conflict — see code: a live release with the same title (existingPressRelease says which), a request with your Idempotency-Key still running, or a release that changed while your request was in flight (e.g. already retracted)
422Idempotency-Key already used with a different request body
500Internal server error

All responses follow the same shape: { "success": bool, "data": ..., "error": "..." }. Check success first, then read data on success or error on failure.

Ready to integrate?

Sign in to your PRNow account and generate your API key.

Get Started