Skip to Content
REST APIWallet & billing

Wallet & billing

Scrapewise is prepaid and pay-as-you-go. Each customer has a wallet balance in euros. Billable calls are charged per page delivered; a page that fails to deliver is not charged, and the same URL is not charged twice within one run. Every new wallet starts with 5 free requests.

When a customer has neither balance nor free requests left, billable endpoints return 402 Payment Required with the error code WALLET_INSUFFICIENT_BALANCE. Clients should treat that code as “prompt the user to top up” rather than as a transient failure.

Price per page delivered: plain €0.00015 · render €0.00075 · super €0.0015 · render + super €0.00375 · Amazon plugins €0.00015 · Google search €0.0015. (Per 1,000 pages: €0.15 · €0.75 · €1.50 · €3.75 · €0.15 · €1.50.)

There are no plan limits — no project, task or seat limits, and no monthly quota. Scraped data is kept for 90 days. Balances do not expire and unused balance is not refunded.

Money unit

Every *Micros field on this surface is an integer count of micro-cents (1 cent = 10,000 µ¢). One plain page at €0.00015 is 150 micro-cents. The single exception is amountCents on the top-up request — that is the Stripe boundary and is expressed in whole cents.

Price list

What you scrapePer page deliveredPer 1,000 pages
Plain page€0.00015€0.15
Render (pages that need a browser)€0.00075€0.75
Super (residential proxy)€0.0015€1.50
Render + Super (the hardest sites)€0.00375€3.75
Amazon plugins€0.00015€0.15
Google search€0.0015€1.50

Scrapewise always tries the cheapest method that works for a site. The live machine-readable catalogue is GET /api/wallet/prices.

Endpoint summary

MethodPathOperation IDAuth scope
GET/api/walletbearer
GET/api/wallet/transactionsbearer
GET/api/wallet/pricesbearer
POST/api/wallet/topupbearer (Firebase sign-in only)
GET/api/wallet/topup/{sessionId}bearer
GET/api/billing-historybearer

Get the wallet — GET /api/wallet

curl -H "Authorization: Bearer $KEY" \ https://portal.scrapewise.ai/api/scraper-api/api/wallet

The wallet is created on first read, with the signup free grant applied.

Response (200)

{ "balanceMicros": 1250000, "currency": "EUR", "freeRequestsRemaining": 5, "lowBalance": false, "lowBalanceThresholdMicros": 500000, "walletBillingEnabled": true }
FieldTypeDescription
balanceMicroslongSigned balance in micro-cents. May be negative after a bounded overshoot on a run already in flight.
currencystringEUR
freeRequestsRemainingintSignup free requests still unused
lowBalancebooleanbalanceMicros < lowBalanceThresholdMicros (exactly at the threshold is not low)
lowBalanceThresholdMicroslongLow-balance threshold, micro-cents
walletBillingEnabledbooleanMaster switch. When false the wallet is informational only and nothing is charged yet.

Errors — 400 (N/A) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500. (The customer is read from the auth principal, so 400/403/404 don’t apply.)

List transactions — GET /api/wallet/transactions

curl -H "Authorization: Bearer $KEY" \ "https://portal.scrapewise.ai/api/scraper-api/api/wallet/transactions?page=0&size=20"

The customer’s own ledger, newest first. Public shape only — no admin audit fields and no margin data.

ParamTypeDescription
pageintZero-indexed page (default 0)
sizeintPage size, 1–100 (default 20)

Response (200)PageDTO<WalletTransactionDTO>. Note this is Scrapewise’s own page envelope: the index field is page, not Spring’s number.

{ "content": [ { "id": "66f0c2...", "type": "CHARGE", "status": "SETTLED", "amountMicros": -30000, "freeRequestsDelta": 0, "balanceAfterMicros": 1220000, "source": "RUN", "jobId": "66f0be...", "scraperId": "66e11a...", "groupId": "66e119...", "lines": [ { "label": "Plain", "pages": 200, "freePages": 0, "unitPriceMicros": 150, "amountMicros": 30000 } ], "createdAt": "2026-09-15T08:12:04Z", "settledAt": "2026-09-15T08:14:41Z" } ], "page": 0, "size": 20, "totalElements": 37, "totalPages": 2 }
FieldTypeDescription
typeenumTOPUP, CHARGE, ADJUSTMENT, FREE_GRANT
statusenumOPEN, SETTLED, APPLIED, EXPIRED, FAILED, UNMATCHED
amountMicroslongSigned micro-cents: TOPUP / positive ADJUSTMENT > 0, CHARGE ≤ 0, FREE_GRANT = 0
balanceAfterMicroslong | nullBalance after this row was applied; null while the row is OPEN
sourceenum | nullWhat produced the charge: RUN, SAMPLE, PREVIEW_URL, PREVIEW_CURL
linesarrayPer-service breakdown — pages, freePages (covered by free requests), unitPriceMicros, amountMicros

Errors — 400 (size outside 1–100, or negative page) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

List scrape prices — GET /api/wallet/prices

curl -H "Authorization: Bearer $KEY" \ https://portal.scrapewise.ai/api/scraper-api/api/wallet/prices

Every enabled sellable service with its customer price per delivered unit. Margin data is never included. Requires a bearer token.

Response (200)

[ { "rowKey": "tier:plain", "kind": "TIER", "label": "Plain", "sellingPriceMicros": 150 }, { "rowKey": "tier:render", "kind": "TIER", "label": "Render", "sellingPriceMicros": 750 } ]
FieldTypeDescription
rowKeystringStable catalogue key, e.g. tier:plain, tier:render, plugin:/plugin/amazon/pdp
kindenumTIER (fetch tier), PLUGIN (dedicated endpoint), DOMAIN (per-domain price)
labelstringHuman-readable name
sellingPriceMicroslongCustomer price per delivered page / plugin call, micro-cents (150 = €0.00015)

Disabled catalogue rows are omitted, so the response is exactly what a customer can be charged for today.

Errors — 400 (N/A) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Start a top-up — POST /api/wallet/topup

POST /api/wallet/topup Authorization: Bearer <firebase-jwt> Idempotency-Key: 8f1c3f6e-1a2b-4c3d-9e8f-0a1b2c3d4e5f Content-Type: application/json { "amountCents": 2000 }

Creates a Stripe Checkout session (mode=payment, EUR) for amountCents (500–100000, i.e. €5–€1,000) and returns its URL and id. Redirect the customer to url.

  • The Idempotency-Key header is mandatory and must be non-blank. A retry with the same key returns the same session (200 plus an Idempotency-Replayed: true response header) instead of opening a second one. Send a fresh UUID per genuine submit.
  • Signed-in (Firebase) principals only. API keys get 403, and so does an admin impersonating another customer via customer-switch — money is never moved on someone else’s behalf.
  • The balance is credited by the Stripe webhook once payment is confirmed, not by this call. Poll GET /api/wallet/topup/{sessionId} after the redirect back.

Response (200)

{ "url": "https://checkout.stripe.com/c/pay/cs_...", "sessionId": "cs_..." }

Errors — 400 (missing/blank Idempotency-Key, or amountCents outside 500–100000) / 401 / 403 (API-key principal, or an admin impersonating another customer) / 404 (N/A) / 429 / 500.

Get top-up status — GET /api/wallet/topup/{sessionId}

curl -H "Authorization: Bearer $KEY" \ https://portal.scrapewise.ai/api/scraper-api/api/wallet/topup/cs_test_a1b2c3

Response (200)

{ "status": "APPLIED" }
StatusMeaning
OPENNot paid yet, or payment still pending
APPLIEDPaid and credited to the balance
EXPIRED / FAILEDNo money moved
UNMATCHEDPaid but not attributable — contact support

Errors — 400 (N/A) / 401 / 403 (N/A) / 404 (unknown session id, or another customer’s session — never treat 404 as “still pending”) / 429 / 500.

Insufficient balance — 402 Payment Required

Every billable launch (scraper run, group run, sample, URL / curl preview and the ad-hoc fetch endpoints) answers with 402 and the code WALLET_INSUFFICIENT_BALANCE when the wallet has neither balance nor free requests left:

{ "errors": [ { "code": "WALLET_INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance — top up to continue" } ] }

Switch on errors[0].code, not on the message. A run stopped mid-flight for the same reason reports stopReason: "WALLET_INSUFFICIENT_BALANCE" on its job-status row. Top up, then retry the call — nothing is lost.

Invoice history — GET /api/billing-history

curl -H "Authorization: Bearer $KEY" \ "https://portal.scrapewise.ai/api/scraper-api/api/billing-history?page=0&size=20&from=2026-01-01&to=2026-05-31"

Paginated Stripe invoice history. Wallet top-ups and per-page charges live in the wallet ledger (GET /api/wallet/transactions), not here. Filters:

ParamTypeDescription
pageintZero-indexed page (default 0)
sizeintPage size (default 20)
statusenumPAID, OPEN, UNCOLLECTIBLE, VOID, DRAFT
fromyyyy-MM-ddFilter to invoices on or after this date (UTC)
toyyyy-MM-ddFilter to invoices on or before this date (UTC)

Response (200) — Spring Page<InvoiceDTO>:

{ "content": [ { "id": "in_...", "amount": 79, "currency": "eur", "status": "PAID", "issuedAt": "2026-04-19T12:00:00Z", "hostedInvoiceUrl": "https://invoice.stripe.com/i/..." } ], "totalElements": 14, "totalPages": 1, "size": 20, "number": 0 }

Errors — 400 (malformed from/to) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Legacy subscription endpoints

The /api/subscription/* endpoints still exist and still respond — they are superseded by the wallet, not removed. New integrations should use the wallet endpoints above; these are documented for clients still calling them.

POST /api/subscription/webhook is Stripe-internal (HMAC-signed) and intentionally excluded from these docs — clients should never call it. It is also the endpoint that credits wallet top-ups, so it stays in place.

Get current subscription — GET /api/subscription

curl -H "Authorization: Bearer $KEY" \ https://portal.scrapewise.ai/api/scraper-api/api/subscription

Response (200)

{ "plan": "PRO", "status": "ACTIVE", "currentPeriodEnd": "2026-06-19T23:59:59Z", "scheduledPlan": null, "payment": null }

scheduledPlan is non-null when a downgrade has been scheduled (effective at currentPeriodEnd). payment is non-null on create/change responses when the customer needs to be redirected to Stripe Checkout.

Errors — 400 (N/A) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500. (The customer is read from the auth principal, so 400/403/404 don’t apply.)

Create subscription — POST /api/subscription/create

POST /api/subscription/create Authorization: Bearer <key> Content-Type: application/json { "plan": "PRO" }

If the customer already has an ACTIVE subscription, the existing subscription is returned with no Checkout URL. Otherwise a new Stripe Checkout Session is created and the URL is returned in payment.url. Redirect the customer to that URL.

Response (200)

{ "plan": "PRO", "status": "INACTIVE", "payment": { "url": "https://checkout.stripe.com/c/pay/cs_..." } }

Errors — 400 (invalid plan) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Change plan — POST /api/subscription/change

POST /api/subscription/change Authorization: Bearer <key> { "plan": "BUSINESS" }

Switch the customer to a different plan. Behaviour depends on direction:

  • Upgrade (higher level): Stripe Checkout URL returned in payment.url; redirect to collect new card / take immediate proration charge.
  • Downgrade: scheduledPlan is set; the actual switch happens at currentPeriodEnd. No immediate charge.
  • No-op (same plan): existing subscription returned, no payment URL.
  • STARTER from INACTIVE: free plan set inline, no Stripe round-trip.
  • STARTER from ACTIVE: schedules downgrade to STARTER at period end.

Errors — 400 (invalid plan / no active subscription) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Cancel subscription — POST /api/subscription/cancel

POST /api/subscription/cancel Authorization: Bearer <key>

Cancels the active subscription at the end of the current billing period. The customer retains access until currentPeriodEnd, then drops to STARTER.

Errors — 400 (no active subscription / no Stripe subscription id) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Cancel scheduled downgrade — POST /api/subscription/cancel-downgrade

POST /api/subscription/cancel-downgrade Authorization: Bearer <key>

Reverts a previously-scheduled downgrade — clears scheduledPlan and restores the Stripe subscription to the current plan’s price.

Errors — 400 (no downgrade scheduled) / 401 / 403 (N/A) / 404 (N/A) / 429 / 500.

Get product prices — GET /api/subscription/prices

curl https://portal.scrapewise.ai/api/scraper-api/api/subscription/prices

No auth — public. Returns the Stripe subscription-product catalogue. These are legacy monthly plan prices, not what a pay-as-you-go customer is charged — see the price list above, or GET /api/wallet/prices.

Response (200)

[ { "plan": "BASIC", "monthly": { "amount": 29, "currency": "eur" } }, { "plan": "PRO", "monthly": { "amount": 79, "currency": "eur" } }, { "plan": "BUSINESS", "monthly": { "amount": 199, "currency": "eur" } } ]

Errors — 400 (N/A) / 401 (N/A — public) / 403 (N/A — public) / 404 (N/A) / 429 / 500. No auth-related codes (this endpoint is public).

See also