API reference
REST API under /v2 — Apify-compatible storage and runs, plus marketplace endpoints.
Bytewells Cloud provides a REST API compatible with the Apify API v2, plus marketplace endpoints for the Store, rentals, credits and publishing (see Marketplace).
Base URL
https://bytewells.com/api/v2Authentication
All requests require a Bearer token in the Authorization header:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://bytewells.com/api/v2/datasetsAll resource endpoints are user-scoped — authenticated users can only access their own resources.
Three token kinds are accepted:
cp_-prefixed API keys (created via the endpoints below, shown in full only once at creation).- JWTs returned by
/v2/auth/loginand/v2/auth/register. - Run-scoped tokens (
rt_<runId>.<signature>) that the runner injects into each Actor container asAPIFY_TOKEN. They act as the run's owner only while the run is active (plus a 10-minute grace period) and only on the storage and run endpoints an Actor needs; every other endpoint returns403.
As an alternative to the Bearer header, a ?token= query parameter is accepted on any authenticated route — useful for browser download links that can't set custom headers.
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/auth/register | Sign up {email, password, name?, username?, referralCode?}, returns user + JWT |
POST | /v2/auth/login | Log in with email/password, returns a JWT |
GET | /v2/auth/apify/start | Begin Login/Connect with Apify (intent=login|connect, PKCE); see Import from Apify |
GET | /v2/auth/apify/callback | OAuth redirect from Apify (sets JWT cookie / connection) |
GET | /v2/auth/me | Get the authenticated user |
POST | /v2/auth/api-keys | Create an API key (raw key returned once) |
GET | /v2/auth/api-keys | List API keys |
DELETE | /v2/auth/api-keys/:keyId | Revoke an API key |
Registration is open when PUBLIC_REGISTRATION=true (the default). Every new user gets a personal organization that holds billing, credits and published listings. The first admin user is created on startup from the ADMIN_EMAIL / ADMIN_PASSWORD environment variables.
Datasets
Store and retrieve scraped data.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/datasets | List all datasets |
POST | /v2/datasets | Create a new dataset |
GET | /v2/datasets/:id | Get dataset details |
DELETE | /v2/datasets/:id | Delete a dataset |
POST | /v2/datasets/:id/items | Push items to dataset |
GET | /v2/datasets/:id/items | Retrieve items |
Push Items
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[{"title": "Item 1"}, {"title": "Item 2"}]' \
https://bytewells.com/api/v2/datasets/{id}/itemsRetrieve Items
Supports pagination with offset and limit query parameters (max limit: 1000).
curl -H "Authorization: Bearer $TOKEN" \
"https://bytewells.com/api/v2/datasets/{id}/items?offset=0&limit=100"Omitting limit returns the entire dataset (matching real Apify), streamed as plain JSON with Apify-style pagination headers — no in-memory materialization on the server, and offset is still honored. Passing an explicit limit uses the paged path. Before v1.6.0 the no-limit case silently defaulted to 100 items; Apify-compatible clients fetching without a limit were truncated.
Pass ?download=1 to stream the entire dataset as a single JSON array file (attachment disposition) — same zero-materialization path.
Key-Value Stores
Store arbitrary data by key.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/key-value-stores | List all stores |
POST | /v2/key-value-stores | Create a new store |
GET | /v2/key-value-stores/:id | Get store details |
DELETE | /v2/key-value-stores/:id | Delete a store |
GET | /v2/key-value-stores/:id/keys | List record keys |
PUT | /v2/key-value-stores/:id/records/:key | Set a record |
GET | /v2/key-value-stores/:id/records/:key | Get a record |
DELETE | /v2/key-value-stores/:id/records/:key | Delete a record |
Common Keys
INPUT— Actor input configurationOUTPUT— Actor output/results
Presigned Record URLs
GET .../records/:key?presigned=1 returns a presigned S3 URL valid for 1 hour instead of the record body — useful for large or binary values (screenshots, big JSON) the browser should fetch directly from object storage.
Request Queues
Manage URLs to crawl with automatic deduplication.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/request-queues | List all queues |
POST | /v2/request-queues | Create a new queue |
GET | /v2/request-queues/:id | Get queue details |
DELETE | /v2/request-queues/:id | Delete a queue |
GET | /v2/request-queues/:id/head | Get next pending requests |
POST | /v2/request-queues/:id/head/lock | Lock and fetch requests |
POST | /v2/request-queues/:id/requests | Add request to queue |
POST | /v2/request-queues/:id/requests/batch | Batch add requests |
GET | /v2/request-queues/:id/requests/:requestId | Get request details |
PUT | /v2/request-queues/:id/requests/:requestId | Update request status |
PUT | /v2/request-queues/:id/requests/:requestId/lock | Prolong request lock |
DELETE | /v2/request-queues/:id/requests/:requestId/lock | Release a lock |
Deduplication
Requests are deduplicated by uniqueKey. Adding a request with an existing uniqueKey is a no-op.
Locking
The lock endpoint (POST .../head/lock) supports distributed crawling. Parameters:
lockSecs— Lock duration in seconds (max 86400)limit— Number of requests to fetch (max 1000)clientKey— Unique identifier for the crawling client
Actors
Manage Actor definitions.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/acts | List all Actors |
POST | /v2/acts | Create an Actor |
GET | /v2/acts/:id | Get Actor details |
PUT | /v2/acts/:id | Update an Actor |
DELETE | /v2/acts/:id | Delete an Actor |
POST | /v2/acts/:id/runs | Start a new run |
POST | /v2/acts/:id/run-sync | Run an Actor and wait for finish |
Wherever :id identifies an Actor, you can pass its id, the name of one of your own Actors, or username~actor-name (Apify style) for a Store Actor.
GET /v2/acts/:id also returns inputSchema, readme, username and, when the Actor is listed in the Store, a listing summary. Users entitled to run an Actor they don't own get a read-only view.
Entitlement and billing
Starting a run (POST /v2/acts/:id/runs, run-sync, reruns and schedules) checks, in order:
- Entitlement. You own the Actor, or its listing is free, or your organization has a live rental (
TRIALING/ACTIVE) or an unexpired time-window pass. On run-start paths only (not run-scoped tokens), if pass auto-purchase is enabled a fare-capped pass may be bought from prepaid credits. Otherwise402with error typerental-required,data.listingSlug, and when the Actor sells passes a machine-readabledata.passesoffer. - Credits. Non-owner runs (and any run by a non-admin) need a positive prepaid credit balance, else
402 insufficient-credits.
Request and response shapes of these endpoints are unchanged from Apify's.
Runs are billed at COMPUTE_PRICE_PER_CU_USD (default $0.15) per compute unit: 1 CU = 1 GB of memory for 1 hour, billed per second with a 60-second minimum.
Deleting Actors
DELETE /v2/acts/:id refuses to delete an Actor that has runs, returning 409 with error type actor-has-runs. Pass ?force=true to cascade-delete the Actor together with its run history (and the runs' webhook deliveries). Force delete still returns 409 (actor-has-active-runs) while any run is READY, RUNNING, or ABORTING — abort those first.
Input Validation
Actor create/update bodies are validated with the following constraints:
name— 1-100 chars, alphanumeric with dots, dashes, underscorestitle— Max 200 charsdescription— Max 5000 charsdefaultRunOptions.timeoutSecs— Max 86400 seconds (24h)defaultRunOptions.memoryMbytes— Max 16384 MB (16 GB)maxRetries— 0-10retryDelaySecs— 1-3600 secondsversion— 1-64 chars, alphanumeric with._+-
The run-dispatch body (POST /v2/acts/:id/runs) uses bare timeout and memory fields instead, with the same 86400-second and 16384-MB caps.
Actor Versions & Builds
Manage source versions and Docker image builds for an Actor.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/acts/:id/versions | List versions |
POST | /v2/acts/:id/versions | Create a version |
GET | /v2/acts/:id/versions/:versionId | Get version details |
DELETE | /v2/acts/:id/versions/:versionId | Delete a version |
GET | /v2/acts/:id/builds | List builds |
POST | /v2/acts/:id/builds | Start a build |
GET | /v2/acts/:id/builds/:buildId | Get build details |
POST | /v2/acts/:id/builds/:buildId/abort | Abort a build |
GET | /v2/acts/:id/builds/:buildId/logs | Get build logs |
Hosted builds from source
POST /v2/acts/:id/builds/source?version=0.0&tag=latest with body application/gzip (a tar.gz of the Actor directory) uploads the source and queues a build on the builder. It returns { data: Build }; poll the build or read its logs until it finishes. Builds fill the Actor's inputSchema, readme and actor.json from .actor/input_schema.json, README.md and .actor/actor.json. bw push uses this endpoint.
Images are named bytewells/<username>/<actor-name> with tags latest and b-<buildId>, prefixed by the image registry in production.
Runs
Monitor Actor executions.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/actor-runs | List all runs |
GET | /v2/actor-runs/:id | Get run status |
PUT | /v2/actor-runs/:id | Update run status |
POST | /v2/actor-runs/:id/abort | Abort a running Actor |
POST | /v2/actor-runs/:id/resurrect | Resurrect a failed run |
GET | /v2/actor-runs/:id/logs | Get run logs |
POST | /v2/actor-runs/:id/logs | Append log entry |
GET | /v2/actor-runs/:id/logs/raw | Download raw log text |
GET | /v2/actor-runs/:id/log | Apify-compatible alias for /logs/raw |
POST | /v2/actor-runs/:id/ingest-crawler-stats | Ingest the run's Crawlee statistics blob into stats |
GET | /v2/actor-runs/:id/dataset/items | Get run's dataset items |
GET | /v2/actor-runs/:id/key-value-store/records/:key | Get run's KV store record |
Platform extensions (not part of the Apify v2 surface)
These endpoints power the dashboard and are safe to use from custom tooling, but don't expect them from apify-client or the Apify platform:
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/actor-runs/:id/rerun | Rerun a terminal (FAILED/ABORTED/TIMED-OUT) run as a new run (since 1.7.0): fresh id and storages, the origin's INPUT bytes, timeout/memory, per-run webhooks, and (within their 24h TTL) envVars are copied; originRunId links back to the first run in the chain. The origin row is untouched. 409 input-not-found when retention already reaped the origin's INPUT; 409 rerun-already-active when a rerun (or runner auto-retry) of the same chain is already queued or running — one active clone per chain, enforced transactionally via a per-chain advisory lock that the runner's auto-retry path takes too, so a manual rerun and an infra retry can't both win. Prefer this over resurrect whenever a webhook consumer keys on the run id. |
GET | /v2/actor-runs/stats | Aggregate status counters (total, running, succeeded, failed) plus failures in the last 24h, in one indexed query. |
GET | /v2/actor-runs/histogram | Hour-bucketed run counts (?hours=24) for the dashboard throughput chart; server-side aggregation, bounded response. |
GET | /v2/actor-runs/:id/cost | Cost analysis for one finished run (since 1.3.0): your cost via actual-overlap droplet attribution, an Apify compute-unit estimate, savings %, and the inputs behind the numbers. 400 run-not-finished before terminal status. Computed on read, never persisted. |
GET | /v2/actor-runs/costs?ids=a,b | Batch your-cost for up to 50 runs (since 1.4.0), answered in two set-based queries. Returns { data: { costs: { [runId]: { yourCostUsd } } } } where yourCostUsd is 0 when there is no billable compute and null when attribution was never recorded. Unknown, other-user, and non-terminal ids are silently omitted; >50 ids is a 400. |
Run Status Values
| Status | Description |
|---|---|
READY | Queued, waiting to start |
RUNNING | Currently executing |
SUCCEEDED | Completed successfully |
FAILED | Execution failed |
ABORTED | Manually stopped |
ABORTING | Abort requested, not yet terminal (read/filter-only — accepted by the list-runs status filter, not by run update) |
TIMED-OUT | Exceeded time limit |
Run response shape (v1.0-committed)
Every endpoint that returns a run (LIST, GET-by-id, PUT, abort, resurrect, rerun) emits the same shape. Notable fields:
| Field | Type | Notes |
|---|---|---|
id | string | Apify-style 21-char nanoid. |
actId | string | Owning actor id. |
status | enum | See "Run Status Values" above. |
defaultDatasetId | string | null | Dataset created at run start; null if the run failed before SDK init. |
defaultDatasetItemCount | number | null | Live count from datasets.item_count, joined per-request. Null when there is no default dataset. Always present on the response — sourced via the centralized RUN_SELECT_WITH_DATASET_COUNT join in the API. Use this when integrating from the dashboard or custom tooling. |
defaultKeyValueStoreId | string | null | Default KV store for the run. |
defaultRequestQueueId | string | null | Default request queue for the run. |
options.timeoutSecs | number | Effective timeout for this run. |
options.memoryMbytes | number | Effective memory limit for this run. |
stats | object | Crawlee SDK statistics ingested at run completion, plus the live dataset item count below for Apify-client compatibility. |
stats.datasetItemCount | number | Same value as defaultDatasetItemCount, defaulted to 0 when there is no dataset. Mirrors Apify's nested location so apify-client consumers reading run.stats.datasetItemCount work unchanged. |
exitCode | number | null | Process exit code from the runner; null while the run is still in flight. |
startedAt / finishedAt | ISO string | Wall-clock timestamps; finishedAt is null until terminal. |
createdAt / modifiedAt | ISO string | Row lifecycle timestamps. |
originRunId | string | null | First run in the chain when this run was produced by the runner's infra auto-retry or by rerun; chains collapse to the original (never a multi-hop walk). Null for first-hand runs. |
retryCount | number | How many infra auto-retries the runner has performed for this run row. Always 0 for runs created via rerun (a rerun is a new run, not a retry). |
Note on webhooks: the webhook payload's resource.stats is built by the runner from the ingested Crawlee statistics blob and does NOT currently mirror stats.datasetItemCount — webhook receivers that need the dataset count should query /v2/actor-runs/:id from the resource.id they receive. This is tracked for parity in v1.0.1.
Webhooks
Receive HTTP callbacks when runs reach a terminal state. Webhooks can be standalone (catalog) or attached to a single run via the webhooks field on run dispatch.
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/webhooks | Create a webhook |
GET | /v2/webhooks | List webhooks (supports scope, runId, runActorId filters) |
GET | /v2/webhooks/:webhookId | Get webhook details |
PUT | /v2/webhooks/:webhookId | Update a webhook |
DELETE | /v2/webhooks/:webhookId | Delete a webhook |
GET | /v2/webhooks/:webhookId/deliveries | List delivery attempts |
POST | /v2/webhooks/:webhookId/test | Send a test delivery |
Supported event types are the four terminal run events:
ACTOR.RUN.SUCCEEDEDACTOR.RUN.FAILEDACTOR.RUN.TIMED_OUTACTOR.RUN.ABORTED
Subscriptions to other Apify event types (e.g. ACTOR.RUN.CREATED) are rejected with a 400.
Schedules
Run Actors automatically on a cron schedule.
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/schedules | Create a schedule |
GET | /v2/schedules | List schedules |
GET | /v2/schedules/:scheduleId | Get schedule details |
PUT | /v2/schedules/:scheduleId | Update a schedule |
DELETE | /v2/schedules/:scheduleId | Delete a schedule |
Users
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/users/me | Get current user (Apify-compatible) |
GET | /v2/users/me/limits | Get account limits |
PUT | /v2/users/me | Update current user (e.g. proxy password) |
GET /v2/users/me uses optional authentication: unauthenticated callers receive an anonymous stub instead of a 401. The Apify SDK calls this endpoint to resolve the proxy password (data.proxy.password) when APIFY_PROXY_PASSWORD is not set in the container environment.
System & Operations
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/system/info | Version, storage health, execution defaults, scaler config |
GET | /v2/system/retention/status | Retention reaper state (admin-only) |
GET | /v2/scaler/status | Auto-scaler status (admin-only) |
GET | /health | Legacy health check |
GET | /health/live | Liveness probe |
GET | /health/ready | Readiness probe — returns 503 when any storage backend (PostgreSQL, Redis, S3) is degraded |
GET | /metrics | Prometheus metrics (admin-only unless METRICS_PUBLIC=true) |
The /health* and /metrics endpoints are served at the root, outside the /v2 prefix.
Marketplace
Bytewells extensions for the Store, rentals, credits and publishing. They are not part of the Apify API.
Money model: compute is prepaid credits kept by Bytewells; passes are one-off debit of those credits with earnings paid to the developer monthly; rentals are Stripe subscription Checkout (programmatic Product+Price) charged on the platform account, with earnings paid to the developer monthly. See Billing and Passes.
Store (public, no auth)
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/store | Search listings. Query: search, category, sortBy (popularity | newest | price), limit, offset |
GET | /v2/store/categories | List categories |
GET | /v2/store/:username/:actorName | Listing detail: README, input schema, pricing, publisher, stats |
Renting
Paid rentals use Stripe Checkout mode=subscription with the listing's Product/Price. Trials use the listing's trialDays (0–30). Body must include consentToShareContact: true.
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/store/:username/:actorName/rent | Start or resume rental Checkout. 200 {checkoutUrl, rentalId} for paid; free listings activate immediately. 409 rental-exists if live. |
GET | /v2/me/rentals | Your organization's rentals |
POST | /v2/me/rentals/:id/cancel | Cancel at period end (Stripe) or end free/trial immediately |
One trial per (listing, renter org), ever. Publisher must be payable (409 publisher-not-payable).
Passes
Passes debit prepaid credits (not Checkout). No card confirmation.
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/store/:username/:actorName/passes | Tiers, active pass, fare-capped quotes, autoPurchaseEnabled |
POST | /v2/store/:username/:actorName/passes | Body {consentToShareContact: true, tier?}. Buys Flash/Burst/Sprint (or MONTHLY) from credits |
GET | /v2/me/passes | Passes your org holds |
GET, PUT | /v2/me/pass-settings | Opt in/out of auto-purchase on run start ({autoPurchase, consentToShareContact?}) |
402 insufficient-credits if the balance cannot cover the charge; 409 publisher-not-payable if the developer has been unable to receive payouts for over 90 days (or is in an unsupported payout country).
Organization and credits
| Method | Endpoint | Description |
|---|---|---|
GET | /v2/me/org | Your personal organization, referral code and link |
PUT | /v2/me/org | Rename the organization |
GET | /v2/me/credits | Available compute credit and ledger |
POST | /v2/billing/credits/checkout | Body :amountUsd (5–1000). Returns Stripe Checkout URL to top up credits |
POST | /v2/billing/portal | Stripe Customer Portal URL (payment methods / invoices for top-ups) |
Credits pay for compute and for pass purchases. The sign-up credit expires after 90 days.
Publishing
| Method | Endpoint | Description |
|---|---|---|
GET, POST | /v2/me/listings | List or create listings (pricingModel: FREE or FLAT_PRICE_PER_MONTH, price, trial, passes) |
GET, PUT | /v2/me/listings/:id | Read or update a listing |
POST | /v2/me/listings/:id/publish | Publish. Paid listings need a payable Connect account |
POST | /v2/me/listings/:id/unpublish | Unpublish |
GET | /v2/me/apify | Apify OAuth connection status (configured, connected, username) — see Import from Apify |
DELETE | /v2/me/apify | Disconnect Apify |
GET | /v2/me/apify/actors | List Actors on the connected Apify account |
POST | /v2/me/apify/import | Body { actorIds, update?, discountPct? } — import selected Actors as drafts |
PUT | /v2/me/payout-country | Body {country} (ISO alpha-2, supported payout countries only). Locked once a Connect account exists |
POST | /v2/billing/connect/onboard | Optional body {country} (must match the payout country). Returns Connect onboarding URL |
GET | /v2/billing/connect/status | Connect account status and payable |
GET | /v2/me/publisher/customers | Renters and pass buyers (name and email, with consent) |
GET | /v2/me/publisher/stats | Listing and rental stats |
GET | /v2/me/publisher/earnings | Earnings totals, per-listing figures, balance and payoutHistory |
Commission on a new rental or pass: 0% if the renter signed up with your referral link, otherwise 10%. Stripe’s card processing and billing fees are deducted from the developer’s earning at cost; Bytewells pays the fixed Connect fees. Earnings are held 14 days, then paid in one monthly transfer once the payable balance reaches $50 (converted to the platform settlement currency, e.g. EUR, at a rate locked on the payout).
Stripe webhook and admin
| Method | Endpoint | Description |
|---|---|---|
POST | /v2/billing/stripe/webhook | Snapshot events (raw body, STRIPE_WEBHOOK_SECRET). Not for API clients |
POST | /v2/billing/stripe/webhook/thin | Thin / Connect v2 events (STRIPE_THIN_WEBHOOK_SECRET when configured) |
GET | /v2/admin/marketplace/metrics | Admin only: GMV, MRR, take rate, listings, paid rentals, sign-ups |