Plans and Limits

Tracore meters usage with two counters — pages (the sum of every successfully-extracted document’s pageCount) and extractions (the count of successful runs). Both reset on the same anniversary monthly window. Limits are enforced server-side. When you hit one, an in-app paywall modal explains which limit fired and links to the upgrade flow.

What counts as a page?

Each document is measured at upload time and stored as pageCount on the document row.

File typePage count
PDFOne page per PDF page (parsed via pdf-lib)
DOCXThe <Pages> value in the document XML
Images (JPEG, PNG)1
Plain text1

The number you see in usage.pagesThisWindow (from GET /user/plan) is the sum of pageCount across documents whose extractions have successfully completed within your current monthly window. The matching usage.extractionsThisWindow field counts how many extraction runs have completed successfully in the same window — Free is capped at 50 successful extractions per month, Pro at 1,000.

When are pages charged?

Pages are charged on the first successful extraction of a document.

  • A failed extraction does not consume any quota.
  • A document whose extraction succeeded counts once per anniversary window — re-extracting it on Pro (with a different schema or model) is free pages-wise.
  • While an extraction is in flight, its pages are reserved in usage.pendingPages so concurrent uploads can’t overshoot the limit. The reservation is released on success or failure.
  • If an extraction worker crashes, an hourly reconciliation job heals the reserved value.

The monthly window

Your quota resets on a per-user anniversary window, anchored on your signup day-of-month. The window is always exactly one month long, with last-day-of-month clamping for short months.

Examples for a user who signed up on January 31:

TodayWindow startWindow end
Feb 15Jan 31Feb 28
Feb 28Feb 28Mar 31
Mar 1Feb 28Mar 31

Examples for a user who signed up on March 15:

TodayWindow startWindow end
Mar 20Mar 15Apr 15
Apr 14Mar 15Apr 15
Apr 16Apr 15May 15

The window block in GET /user/plan returns the live start, end, and daysRemaining.

Free plan

LimitValue
Workspaces1
Schemas3
Pages per month50
Extractions per month50
Environmentsproduction only
WebhooksEnabled
Re-extractionDisabled
  • The staging and development environments show a lock icon in the env selector. Selecting one redirects you to the /upgrade page.
  • Re-running extraction on a previously-counted document requires Pro.
  • All plan checks return HTTP 403 plan_limit_exceeded with a discriminator that drives the in-app modal.

Pro plan — $19 / month

LimitValue
WorkspacesUnlimited
SchemasUnlimited
Pages per month2,000
Extractions per month1,000
Environmentsproduction, staging, development
WebhooksEnabled
Re-extractionEnabled

Upgrades happen through Stripe Checkout. Returning to the app at /?upgraded=true triggers a short poll on GET /user/plan; the plan pill flips to Pro within 30 seconds without a manual refresh.

When you upgrade mid-window, your existing usage is preserved against the new limit. A user at 100/100 pages on Free who upgrades immediately has 1,900 pages remaining — the window does not reset.

When you hit a limit

The api returns HTTP 403 with a structured body:

{
  "error": {
    "code": "plan_limit_exceeded",
    "limit": "max_pages_per_month",
    "plan": "free",
    "limitValue": 50,
    "currentValue": 50,
    "windowStart": "2026-04-01T00:00:00Z",
    "windowEnd":   "2026-05-01T00:00:00Z",
    "message": "Free plan allows 100 pages per month. Upgrade to Pro for 2,000.",
    "upgradeUrl": "/upgrade"
  }
}

The web app maps the limit discriminator to one of six modal copy variants:

limitTriggered by
max_workspacesCreating a 2nd workspace on Free
max_schemasCreating a 4th schema on Free
max_pages_per_monthAn extraction whose pages would push you over the window total
max_extractions_per_monthAn extraction that would exceed your monthly successful-run count
env_not_allowedSelecting staging / development on Free
re_extract_not_allowedRe-extracting an already-counted document on Free

Inspecting your plan

import { TracoreClient } from "@tracore/sdk";

const client = new TracoreClient({ apiKey: process.env.TRACORE_API_KEY! });
const plan = await client.user.getPlan();

console.log(plan.plan);                          // "free" | "pro"
console.log(plan.usage.pagesThisWindow);         // 37
console.log(plan.usage.extractionsThisWindow);   // 12
console.log(plan.usage.pendingPages);            // 0
console.log(plan.window.daysRemaining);          // 14

The same data drives the plan pill in the dashboard header. The pill turns amber once you cross 80 % of your page limit.