Flipazine API

Upload PDFs and manage flipbooks programmatically — built for integrations like publishing a flipbook from a content management system (e.g. MemberGate) and embedding it on a page.

Base URL: https://flipazine.com/api/v1

Every endpoint is listed below with its full path. All responses are JSON and wrap their payload in a data property (except the plain { "success": true } acknowledgements and error objects).

Authentication

Every request needs an API key in the Authorization header. Create one from Account → API keys — the raw key is shown once at creation time and can't be retrieved again after that.

Authorization: Bearer fz_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API access is a plan feature — a valid key still returns 403 if the account's current plan doesn't include API access. Team members' keys act on their team's shared account, not a separate one of their own. Check capabilities.apiAccess from GET /account to confirm before relying on the API.

Rate limits

Each account gets a per-minute request budget shared across all of its API keys — set by the account's plan, and occasionally raised for specific integration partners on request. Every response includes headers showing where you stand:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 2026-07-03T14:32:00.000Z

Going over the limit returns 429 Too Many Requests with the same headers — back off until X-RateLimit-Reset. Need a higher limit for a high-volume integration? Get in touch.

Response shape & fields

A successful call returns { "data": ... }. For a single flipbook data is a flipbook object; for the list it's an array of them. Every flipbook object contains these fields:

FieldTypeNotes
idstringThe flipbook's unique id — used in every /flipbooks/{id} path and in its URLs
titlestring
descriptionstring | null
originalFilenamestringThe uploaded file's name
statusstringpendingprocessingready, or failed
errorMessagestring | nullSet only when status is failed
pageCountnumber0 until processing finishes
isPublicbooleanPublic flipbooks are viewable and embeddable by anyone; private ones only where allowed (see Embedding)
viewCountnumberTotal views (excludes the owner previewing)
allowSearch, allowFullscreen, allowShare, allowSound, allowDownload, allowContents, allowPrint, allowAutoplaybooleanViewer feature toggles — see Upload
layoutstringOne of the six layouts
coverStylestringhard or soft — only affects the page-turning layouts
embedDomainsstring[]Hostnames a private flipbook may be embedded on (see Embedding)
pdfUrlstring | nullThe original uploaded PDF. null until status is ready. See The original PDF
thumbnailUrlstring | nullCover-page thumbnail image. null until ready
viewerUrlstringShareable full page (/f/{id}) — link to it, don't frame it
embedUrlstringBare flipbook (/embed/{id}) — what the iframe points at
embedCodestringReady-made <iframe> snippet for embedUrl
createdAt, updatedAtstringISO 8601 timestamps

List vs. single: GET /flipbooks/{id} additionally includes pages, links and outline arrays (the page images, in-page hyperlinks and table of contents). The list endpoint omits those for brevity. Responses may also carry internal storage keys (pdfKey, thumbnailKey); prefer pdfUrl / thumbnailUrl, which are the fully-qualified URLs.

Account, plan & defaults

GET /account — what the account is allowed to do, what it's using, and what its new flipbooks start with. Use capabilities to decide what to offer before you try (rather than attempting a call and handling a 403).

curl https://flipazine.com/api/v1/account \
  -H "Authorization: Bearer fz_..."
{
  "data": {
    "email": "publisher@example.com",
    "plan": { "key": "pro", "name": "Pro" },
    "subscription": { "status": "active", "currentPeriodEnd": "2026-08-01T00:00:00.000Z" },
    "capabilities": {
      "apiAccess": true,
      "replacePdf": true,          // whether POST /flipbooks/{id}/replace will work
      "monthlyFlipbookLimit": 100, // -1 means unlimited
      "storageLimitMB": 5000,
      "maxTeamMembers": 5,
      "apiRateLimitPerMinute": 120
    },
    "usage": {
      "flipbooks":   { "used": 12, "limit": 100 },
      "storageBytes":{ "used": 734003200, "limitBytes": 5242880000 },
      "teamMembers": { "used": 2, "limit": 5 },
      "apiAccess": true
    },
    "flipbookDefaults": {
      "isPublic": false,
      "layout": "spread", "coverStyle": "hard",
      "allowSearch": true, "allowContents": true, "allowFullscreen": true,
      "allowShare": true, "allowPrint": true, "allowDownload": false,
      "allowSound": true, "allowAutoplay": false,
      "embedDomains": ["members.example.com"]
    }
  }
}

flipbookDefaults are the settings a new flipbook gets when the upload doesn't say otherwise — mirror them in your own form so what you show matches what you'd actually create. Change them with PATCH /account (only affects flipbooks created afterwards; existing ones are untouched):

curl -X PATCH https://flipazine.com/api/v1/account \
  -H "Authorization: Bearer fz_..." \
  -H "Content-Type: application/json" \
  -d '{"allowDownload": false, "embedDomains": ["members.example.com"]}'

Accepts any of the same fields as the defaults object above; send only the ones you want to change. The body may be the fields directly (as shown) or wrapped in a flipbookDefaults object. Returns the updated defaults.

List flipbooks

GET /flipbooks

curl https://flipazine.com/api/v1/flipbooks \
  -H "Authorization: Bearer fz_..."

Returns every flipbook on the account, most recent first.

{
  "data": [
    {
      "id": "clx1a2b3c...",
      "title": "Spring Catalogue",
      "description": null,
      "status": "ready",
      "pageCount": 24,
      "isPublic": true,
      "embedDomains": [],
      "layout": "spread",
      "coverStyle": "hard",
      "allowSearch": true,
      "allowDownload": false,
      "pdfUrl": "https://cdn.flipazine.com/pdfs/clx1a2b3c....pdf",
      "thumbnailUrl": "https://cdn.flipazine.com/thumbs/clx1a2b3c....png",
      "viewerUrl": "https://flipazine.com/f/clx1a2b3c...",
      "embedUrl": "https://flipazine.com/embed/clx1a2b3c...",
      "embedCode": "<iframe src=\"https://flipazine.com/embed/clx1a2b3c...\" width=\"100%\" height=\"700\" style=\"border:none;\" allow=\"fullscreen\" allowfullscreen></iframe>",
      "createdAt": "2026-06-01T10:00:00.000Z",
      "updatedAt": "2026-06-01T10:03:00.000Z"
      /* ...plus every other field from the reference above */
    }
  ]
}

Upload a PDF (create a flipbook)

POST /flipbooksmultipart/form-data

Every field except title and pdfFile is optional. Anything you omit falls back to the account's default for that setting (see flipbookDefaults), not a fixed value — so the effective default is whatever GET /account reports. The "Account default" column below shows the out-of-the-box value on a fresh account.

FieldRequiredAccount defaultNotes
titleYesFlipbook title
pdfFileYesThe PDF file
descriptionNo
isPublicNofalse"true"/"false". When private, set embedDomains to allow embedding
allowSearchNotrueFull-text search
allowContentsNotrueTable-of-contents panel
allowFullscreenNotrueFullscreen button
allowShareNotrueShare / copy-link button
allowPrintNotruePrint button
allowDownloadNofalseIn-viewer button to download the original PDF
allowSoundNotruePage-turn sound
allowAutoplayNofalseAuto-advancing slideshow
layoutNospreadOne of the six layouts
coverStyleNohardhard/soft — only affects page-turning layouts
embedDomainsNoaccount defaultComma-, space- or newline-separated hostnames (max 20). See Embedding
curl https://flipazine.com/api/v1/flipbooks \
  -H "Authorization: Bearer fz_..." \
  -F "title=Spring Catalogue" \
  -F "pdfFile=@catalogue.pdf" \
  -F "isPublic=false" \
  -F "embedDomains=members.example.com, www.example.com"

Returns 201 immediately with status: "pending" — the PDF is converted to a flipbook in the background. Poll GET /flipbooks/{id} until it's ready.

Get a flipbook / poll status

GET /flipbooks/{id}

A flipbook moves through pendingprocessingready (or failed). Poll this endpoint every few seconds after upload until status is "ready", then use viewerUrl, embedCode or pdfUrl from the response. This response also includes the pages, links and outline arrays (see the field reference).

curl https://flipazine.com/api/v1/flipbooks/clx1a2b3c... \
  -H "Authorization: Bearer fz_..."

Update a flipbook's settings

PATCH /flipbooks/{id}application/json. Change settings without touching the PDF. Send only the fields you want to change; anything you omit is left alone. Returns the updated flipbook and takes effect immediately — no reprocessing.

curl -X PATCH https://flipazine.com/api/v1/flipbooks/clx1a2b3c... \
  -H "Authorization: Bearer fz_..." \
  -H "Content-Type: application/json" \
  -d '{"allowDownload": false, "embedDomains": ["members.example.com", "www.example.com"]}'

Accepts any of:

FieldTypeNotes
titlestring1–255 chars
descriptionstring | nullUp to 2000 chars; null clears it
isPublicboolean
allow*booleanAny of the eight viewer toggles
layoutstringOne of the six layouts
coverStylestringhard / soft
embedDomainsstring[]Array of hostnames (max 20). Normalised server-side — scheme/path/port stripped, lowercased, de-duplicated

Table of contents

A flipbook's contents are read from the PDF on upload — from its bookmarks if it has them, otherwise by parsing a printed contents page. That parse is a heuristic, so it can pick up stray text or miss entries. These endpoints let you correct it.

GET /flipbooks/{id}/outline — the current table, in display order:

curl https://flipazine.com/api/v1/flipbooks/clx1a2b3c.../outline \
  -H "Authorization: Bearer fz_..."
# → { "data": [ { "title": "Editor's Message", "pageNumber": 3, "depth": 0 }, ... ] }

PUT /flipbooks/{id}/outline replaces the whole table — array order is the display order:

curl -X PUT https://flipazine.com/api/v1/flipbooks/clx1a2b3c.../outline \
  -H "Authorization: Bearer fz_..." \
  -H "Content-Type: application/json" \
  -d '{"entries": [
        {"title": "Editor Message", "pageNumber": 3, "depth": 0},
        {"title": "Case Report", "pageNumber": 4, "depth": 0},
        {"title": "Method", "pageNumber": 5, "depth": 1}
      ]}'

depth (0–3) indents an entry under the one above. title is 1–300 chars, pageNumber must be within the document, and there's a limit of 500 entries. Send {"entries": []} to clear the contents entirely. Returns the saved table.

POST /flipbooks/{id}/outline re-reads the contents from the stored PDF. This discards any edits — it's the "start again from what the PDF says" button:

curl -X POST https://flipazine.com/api/v1/flipbooks/clx1a2b3c.../outline \
  -H "Authorization: Bearer fz_..."

The rescan runs in the background; poll GET to see the result. Note that replacing a flipbook's PDF does not touch its contents — a scan is a guess, and it would be wrong to overwrite a corrected table with a fresh guess on every new edition. Rescan explicitly if you want the new PDF's contents.

Replace the PDF

POST /flipbooks/{id}/replacemultipart/form-data with a single pdfFile field. Upload a new edition of an existing flipbook. The flipbook keeps its id, so its viewerUrl, embedUrl and every page it's already embedded on keep working and simply start showing the new version. Requires a plan that includes PDF replacement (capabilities.replacePdf); otherwise returns 403.

curl -X POST https://flipazine.com/api/v1/flipbooks/clx1a2b3c.../replace \
  -H "Authorization: Bearer fz_..." \
  -F "pdfFile=@march-2026-issue.pdf"

Conversion runs in the background exactly as it does for a new upload: the flipbook goes back to status: "processing" and keeps serving the previous edition until the new one is ready, so poll GET /flipbooks/{id} until it reads ready again. Once ready, pdfUrl points at the new edition.

Replacing doesn't count against your monthly flipbook allowance — it isn't a new flipbook. The previous edition's pages are removed as part of the swap, so its storage is released too.

Delete a flipbook

DELETE /flipbooks/{id} — permanent; removes the flipbook and its stored files.

curl -X DELETE https://flipazine.com/api/v1/flipbooks/clx1a2b3c... \
  -H "Authorization: Bearer fz_..."
# → { "success": true }

GET /flipbooks/{id}/search?q=your+search+term

curl "https://flipazine.com/api/v1/flipbooks/clx1a2b3c.../search?q=pricing" \
  -H "Authorization: Bearer fz_..."

Returns matches with a snippet and the position of the hit, ordered by page:

{
  "data": [
    {
      "pageNumber": 12,
      "snippet": "...our 2026 pricing is held flat for existing...",
      "matchStart": 9,
      "matchLength": 7,
      "wholeWord": true,
      "rects": [ { "xPct": 12.4, "yPct": 30.1, "widthPct": 8.0, "heightPct": 2.2 } ]
    }
  ]
}

rects give the match's location on the page as percentages (empty if the flipbook was processed before positions were captured, or the page has no text layer).

Layouts

The layout field (settable on upload and via PATCH) picks how the flipbook is displayed:

ValueDescriptionCover style?
spreadTwo pages side by side with 3D page turns (drops to a single page when there isn't room). The default.Yes
singleOne page at a time with 3D page turns.Yes
scrollPages stacked vertically, scrolled like a document. Best for long reads and small screens.No
slidesOne page at a time, sliding across. Suits decks, posters and menus.No
carouselA 3D carousel with neighbouring pages angled either side.No
rolodexThe carousel turned on its side — pages stacked above and below.No

coverStyle (hard/soft) only affects the page-turning layouts (spread, single); it's stored but has no visible effect on the others.

The original PDF

Every flipbook response includes pdfUrl — a direct link to the original PDF that was uploaded (or, after a replace, the current edition). It's null until the flipbook's status is ready, since the original is stored during background processing.

pdfUrl is returned to your (authenticated) integration regardless of the allowDownload setting — that toggle only controls whether end viewers see a download button in the flipbook itself. Treat pdfUrl as the source-of-truth link to the original document; pair it with thumbnailUrl for the cover image.

Embedding a flipbook

Once a flipbook is ready, drop the embedCode from the API response straight into a page — for example a MemberGate content page:

Note the two different URLs. viewerUrl (/f/…) is the shareable page, with the site header, title and description around the flipbook — link to it, don't frame it. embedUrl (/embed/…) is the flipbook on its own, with no Flipazine chrome, and it's what embedCode points at.

Public vs. private

A public flipbook (isPublic: true) can be embedded anywhere and opened directly.

A private flipbook (isPublic: false) is not viewable by a direct link and not listed anywhere — but it can be embedded on the hostnames you list in embedDomains. When the embed loads, the browser tells Flipazine which page framed it (via the Referer header); if that hostname is on the flipbook's embedDomains list, the flipbook displays, otherwise the frame shows a 404. This is what lets a members-only publication live on a members' site without being readable, findable or embeddable anywhere else.

To embed a private flipbook, the hosting page must…
Be served from a hostname on the flipbook's embedDomains (exact match, case-insensitive — no wildcards; list www and bare domains and each subdomain separately)
Be served over HTTPS
Send a referrer — the browser default is fine, but a Referrer-Policy: no-referrer or same-origin on the page will break the embed

Set embedDomains on upload, per-flipbook via PATCH, or account-wide as a default via PATCH /account. This is an access-scoping control, not hard DRM — the page images are served from a CDN, so it stops casual sharing and off-site embedding rather than a determined downloader who already has the id.

Background colour

The embed has a white background by default, so it sits neutrally in your page. Override it with a bg query param — a hex colour, with or without the #:

<iframe
  src="https://flipazine.com/embed/clx1a2b3c...?bg=f5f5f5"
  width="100%" height="700" style="border:none;"
  allow="fullscreen" allowfullscreen></iframe>

Anything that isn't a valid hex colour is ignored and the default is used. The viewer's controls switch between light and dark ink automatically to stay readable on the colour you pick.

Iframe height

The flipbook fits whatever height you give it. The viewer measures the frame and scales the book to fit both its width and its height, so a fixed height="700" won't overflow or produce inner scrollbars — the book is simply drawn smaller. You don't have to work out a per-flipbook height.

If you'd rather the frame matched the flipbook's natural size, the embed posts the height it would like to the parent window whenever that changes:

window.addEventListener("message", (event) => {
  if (event.origin !== "https://flipazine.com") return;
  const msg = event.data;
  if (msg?.type !== "flipazine:resize") return;

  // msg.flipbookId — which flipbook, if you embed more than one
  // msg.height    — the height it would like, in px
  document.querySelector("#my-flipbook-iframe").style.height = msg.height + "px";
});

The reported height is the flipbook's natural height at the frame's current width. Applying it settles immediately: the frame becomes exactly tall enough, the book renders at full size, and the next message reports the same value.

Pages stop shrinking at a readable minimum, so a very short frame (under roughly 400px for a portrait page) can still overflow — a book too small to read is worse than a scrollbar. Always check event.origin before acting on a message.

Errors

Errors are always a JSON object with an error message and an appropriate status code:

{ "error": "Invalid or missing API key" }
StatusMeaning
400Missing/invalid fields, or the uploaded file isn't a valid PDF
401Missing or invalid API key
403Plan doesn't include API access or PDF replacement, or a plan limit (flipbook count / storage) has been reached
404Flipbook not found, or not owned by this key
413File too large
429Rate limit exceeded — see X-RateLimit-Reset header

Full walkthrough

A typical integration — e.g. publishing from a CMS and embedding the result on a members' content page:

# 1. (Optional) Check what the account can do and its defaults
curl https://flipazine.com/api/v1/account -H "Authorization: Bearer fz_..."

# 2. Upload the PDF — private, embeddable only on the members' site
curl https://flipazine.com/api/v1/flipbooks \
  -H "Authorization: Bearer fz_..." \
  -F "title=Spring Catalogue" \
  -F "pdfFile=@catalogue.pdf" \
  -F "isPublic=false" \
  -F "embedDomains=members.example.com"
# → { "data": { "id": "clx1a2b3c...", "status": "pending", ... } }

# 3. Poll until ready (repeat every few seconds)
curl https://flipazine.com/api/v1/flipbooks/clx1a2b3c... \
  -H "Authorization: Bearer fz_..."
# → { "data": { "status": "ready", "embedCode": "<iframe ...>", "pdfUrl": "https://...", ... } }

# 4. Render embedCode on the members' page (served from members.example.com over HTTPS)

Questions or need a rate limit raised for a high-volume integration? Get in touch.