Polycam Reconstruction API

Create captures from uploaded session data and run Polycam cloud reconstruction.

The Polycam Reconstruction API lets approved Enterprise integrations create a capture, upload a session.zip archive, and queue Polycam cloud reconstruction. Reconstruction jobs use prepaid API credits and are subject to workspace rate limits.

Limited Availability Reconstruction API access is enabled per workspace by Polycam. Once your workspace has access, every API token in the workspace carries the reconstruct scope, including tokens created earlier. Contact Polycam to enable access for your workspace.

Base URL

All API v1 endpoints are served under:

https://poly.cam/api/v1

Token management endpoints (create, list, revoke) live on the main application API under a separate path:

https://poly.cam/api/account/api-tokens

Authentication

API Token Authentication

Reconstruction endpoints require a bearer token in the Authorization header. Tokens are prefixed with poly_ followed by 64 hexadecimal characters.

Authorization: Bearer poly_abc123...def456

Key facts about API tokens:

Firebase User Authentication (Token Management Endpoints)

The token management endpoints (create / list / revoke) use Firebase authentication. Pass a valid Firebase ID token in the Authorization header:

Authorization: Bearer <firebase-id-token>

The authenticated user must be the owner of the target workspace (for personal workspaces) or have owner-level access to the organization.

Errors

The API uses conventional HTTP status codes and returns errors as JSON:

{
  "error": "Human-readable error message"
}
StatusMeaning
200Success
202Accepted — request accepted, processing asynchronously
400Bad Request — invalid parameters or body
401Unauthorized — missing or invalid authentication
403Forbidden — valid credentials but insufficient permissions
404Not Found — resource does not exist or is not in your workspace
409Conflict — a conflicting operation is already in progress
422Unprocessable Entity — resource exists but cannot be processed as requested
429Too Many Requests — rate limit or concurrent job limit exceeded
500Internal Server Error

Common 401 error messages

MessageCause
Missing Authorization headerNo Authorization header sent
Bearer authorization requiredHeader doesn't start with Bearer
Invalid API token formatToken doesn't start with poly_
Invalid API tokenToken not recognized
API token has been revokedToken was previously revoked
API token has expiredToken's expiration date has passed

Common 403 error messages

MessageCause
Token does not have reconstruct scopeReconstruction API access is not enabled for the workspace; contact Polycam
API access is not enabled for this workspaceEvery API capability has been disabled for the workspace, so its tokens have no scopes; contact Polycam

Token Management

These endpoints let you create, list, and revoke API tokens for a workspace. They use Firebase user authentication (not API token auth).

Create an API Token

POST /api/account/api-tokens Firebase Auth

Creates a new API token for the specified workspace. The raw token value is returned only in this response — store it immediately.

Request Body

FieldTypeRequiredDescription
workspace object Yes Target workspace. Contains id (string) and type ("user" or "org").
name string Yes Human-readable label (1–100 characters).
expiresInDays number No Number of days until the token expires. Omit for a non-expiring token.

Example Request

curl -X POST https://poly.cam/api/account/api-tokens \
  -H "Authorization: Bearer <firebase-id-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace": { "id": "ws_abc123", "type": "org" },
    "name": "CI Pipeline Token",
    "expiresInDays": 90
  }'

Response 200

{
  "token": "poly_a1b2c3d4e5f6...64 hex chars",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "CI Pipeline Token",
  "prefix": "poly_a1b2c3d4",
  "scopes": ["content_management", "reconstruct"],
  "createdAt": 1706140800000,
  "expiresAt": 1713916800000,
  "message": "Save this token now - it will not be shown again!"
}
Important The token field contains the full raw token. This is the only time it will be returned. Store it in a secure location (e.g. a secrets manager).

Error Responses

StatusReason
400Invalid user account
401Missing or invalid Firebase authentication
403User does not have permission to create tokens for this workspace
403API access is not enabled for this workspace, so there are no scopes to mint

List API Tokens

GET /api/account/api-tokens Firebase Auth

Lists all API tokens for a workspace. Raw token values are never returned — only the prefix (first 13 characters) is shown.

Query Parameters

ParameterTypeRequiredDescription
workspaceId string Yes The workspace ID.
workspaceType string Yes "user" or "org"

Example Request

curl https://poly.cam/api/account/api-tokens?workspaceId=ws_abc123&workspaceType=org \
  -H "Authorization: Bearer <firebase-id-token>"

Response 200

{
  "tokens": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "CI Pipeline Token",
      "prefix": "poly_a1b2c3d4",
      "scopes": ["content_management", "reconstruct"],
      "createdAt": 1706140800000,
      "createdBy": {
        "id": "user_xyz",
        "username": "alice"
      },
      "expiresAt": 1713916800000,
      "revoked": false,
      "revokedAt": undefined
    }
  ]
}

Error Responses

StatusReason
401Missing or invalid Firebase authentication
403User does not have permission to view tokens for this workspace

Revoke an API Token

DELETE /api/account/api-tokens/:tokenId Firebase Auth

Revokes an API token. Revoked tokens can no longer be used for authentication. This is a soft delete — the token record is preserved with a revokedAt timestamp.

Path Parameters

ParameterTypeDescription
tokenId string The ID of the token to revoke.

Example Request

curl -X DELETE https://poly.cam/api/account/api-tokens/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <firebase-id-token>"

Response 200

{
  "success": true,
  "message": "Token revoked"
}

If the token was already revoked:

{
  "success": true,
  "message": "Token already revoked"
}

Error Responses

StatusReason
400Invalid user account
401Missing or invalid Firebase authentication
403User does not have permission to revoke this token
404Token not found

Credits

Credits endpoints let API-token callers inspect the prepaid balance for the token's workspace and review the ledger of deposits, charges, and refunds. These endpoints are read-only and use API token authentication. To add credits, use the API Keys page in the Polycam dashboard.

Get Credit Balance

GET /v1/credits API Token

Returns the current prepaid API credit balance for the authenticated token's workspace.

Example Request

curl https://poly.cam/api/v1/credits \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "balanceCredits": 1500,
  "updatedAt": 1706140800000
}

Error Responses

StatusReason
401Missing or invalid API token

Get Credit Ledger

GET /v1/credits/ledger API Token

Returns a paginated ledger of credit changes for the authenticated token's workspace, ordered by creation time with newest entries first.

Query Parameters

ParameterTypeDefaultDescription
limit integer 50 Number of ledger entries per page. Min: 1, Max: 200.
cursor string — Pagination cursor from a previous response's cursor field.

Ledger Entry Fields

FieldDescription
kind"deposit", "charge", "refund", or "adjustment".
amountCreditsPositive for deposits/refunds; negative for charges.
balanceAfterCreditsWorkspace balance after this ledger entry was applied.
endpointAPI endpoint associated with a charge or refund, when applicable.
referenceIdInternal audit/debug reference, such as a job id or original ledger entry id, when applicable.

Example Request

curl https://poly.cam/api/v1/credits/ledger?limit=25 \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "entries": [
    {
      "id": "ledger_abc123",
      "kind": "charge",
      "amountCredits": -1050,
      "balanceAfterCredits": 450,
      "createdAt": 1706140800000,
      "endpoint": "POST /api/v1/captures",
      "description": "Reconstruction (splat)"
    },
    {
      "id": "ledger_def456",
      "kind": "deposit",
      "amountCredits": 1500,
      "balanceAfterCredits": 1500,
      "createdAt": 1706137200000,
      "description": "Credit purchase"
    }
  ],
  "cursor": "1706137200000"
}

When there are no more entries, the cursor field is omitted from the response.

Error Responses

StatusReason
401Missing or invalid API token

Create a Capture

POST /v1/captures API Token

Creates a new capture, reserves its reconstruction job in waitingForUpload, and returns a signed upload URL for the session.zip file. Upload to this URL, then call Confirm session.zip Upload to make the job runnable.

Scope required This endpoint requires the reconstruct scope on the API token.

Request Body

FieldTypeRequiredDescription
name string No Display name for the capture (max 256 characters). Defaults to empty string.
externalId string No External reference ID for linking to external systems (max 256 characters).
mode string Yes Reconstruction mode: "photo" or "splat".
numKeyframes integer Yes Number of image keyframes for the reconstruction. Must be at least 20 and must exactly match the number of files in session.zip under keyframes/images/.
totalSize integer No Byte size of the session.zip you will upload. Provide it to receive a multipart upload (pre-signed part URLs) instead of a single PUT URL — recommended for large (multi-gigabyte) uploads. Must be a positive integer.

Example Request

curl -X POST https://poly.cam/api/v1/captures \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Office Scan", "externalId": "JOB-123", "mode": "photo", "numKeyframes": 20 }'

Response 201

{
  "capture": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Office Scan",
    "externalId": "JOB-123",
    // ... same shape as the Capture Object
  },
  "jobId": "7f4f2b6c-0a5d-4b5a-9f1f-0f3d0d3f9a12",
  "mode": "photo",
  "status": "pending",
  "upload": {
    "url": "https://storage.example.com/signed-upload-url?token=..."
  },
  "charge": {
    "amountCredits": 600,
    "ledgerEntryId": "ledger_abc123"
  }
}

PUT your session.zip file to the upload.url. When you send totalSize, upload is instead a multipart descriptor (id, partSize, and pre-signed parts[].url values).

Error Responses

StatusReason
400Invalid or missing mode, invalid numKeyframes, invalid totalSize, name exceeds 256 characters, or externalId exceeds 256 characters
401Missing or invalid API token
402Insufficient API credits
403Token does not have the reconstruct scope
429Rate limit or concurrent job limit exceeded (see Rate Limits)

Session Upload

Creating a capture reserves reconstruction in waitingForUpload. Upload session.zip to the signed URL returned by Create Capture, then confirm the upload to make the job runnable. Use webhooks or poll the capture to track processing.

session.zip can be multiple gigabytes. To upload it as parallel parts instead of a single PUT, send totalSize (the archive's byte size) when creating the capture or when refreshing the upload URL. Recommended for any large (multi-gigabyte) upload — and necessary for archives that exceed the storage provider's single-request size limit. The upload object (or the refresh response) is then a multipart descriptor instead of a single URL:

{
  "id": "2~abc123...",
  "partSize": 8388608,
  "parts": [
    { "url": "https://storage.example.com/...&partNumber=1" },
    { "url": "https://storage.example.com/...&partNumber=2" }
  ]
}

Split the archive into partSize-byte chunks (the final part may be smaller), PUT each chunk to the matching parts[].url, and keep the ETag response header returned by each. Then confirm the upload, passing the collected tags as upload. The parts array order is the part number: parts[0] is part 1, parts[1] is part 2, and so on.

{
  "upload": {
    "id": "2~abc123...",
    "parts": [
      { "etag": "\"e1...\"" },
      { "etag": "\"e2...\"" }
    ]
  }
}
Splat mode requires Polycam frames.json inside session.zip Gaussian splatting needs per-photo camera poses and intrinsics. The archive must contain a Polycam-format frames.json file at the zip root, alongside the keyframes/ directory. Put the source photos under keyframes/images/, and make each frame's name match the photo filename. This is Polycam's own format, not COLMAP or NeRF transforms.json. Photo mode does not require this file.

Download session.zip

GET /v1/captures/:captureId/session.zip API Token

Returns a signed download URL for the capture's source session.zip archive. For captures with source images, the archive contains the raw keyframe files under keyframes/images/.

Tokens with only reconstruct can download session archives for captures created through the API.

Example Request

curl https://poly.cam/api/v1/captures/550e8400-e29b-41d4-a716-446655440000/session.zip \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "url": "https://storage.example.com/signed-session-zip-url?token=...",
  "expiresIn": 3600
}

Error Responses

StatusReason
401Missing or invalid API token
404Capture not found or not accessible to this token

Refresh session.zip Upload URL

POST /v1/captures/:captureId/session.zip API Token

Returns a fresh upload target for the capture's session.zip. Use this if the original URL expires or an upload needs to be retried.

Scope required This endpoint requires the reconstruct scope on the API token.

Request Body

Optional — send no body for a single PUT URL.

FieldTypeRequiredDescription
totalSize integer No Byte size of the session.zip. Provide it to start a multipart upload (the response is the multipart descriptor instead of { url }). Must be a positive integer.

Example Request

curl -X POST https://poly.cam/api/v1/captures/550e8400-e29b-41d4-a716-446655440000/session.zip \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "url": "https://storage.example.com/signed-upload-url?token=..."
}

When totalSize is sent, the response is instead a multipart descriptor (id, partSize, and parts[].url).

Error Responses

StatusReason
400totalSize is not a positive integer
401Missing or invalid API token
403Token does not have the reconstruct scope
404Capture not found or does not belong to your workspace

Confirm session.zip Upload

PUT /v1/captures/:captureId/session.zip API Token

Confirms that the capture's session.zip upload has completed. This records the upload metadata and wakes the reconstruction job that was waiting for the file. For single-PUT uploads, Polycam can also confirm server-side when storage reports that the object was created. Multipart uploads must be confirmed with the collected ETag values so Polycam can assemble the final object.

Scope required This endpoint requires the reconstruct scope on the API token.

Request Body

Optional — send no body to confirm a single-PUT upload. For a multipart upload, send upload to assemble the parts into the final object.

FieldTypeRequiredDescription
upload object Multipart only The multipart bundle: { "id": string, "parts": [{ "etag": string }] }, where id is from the multipart descriptor and each etag is the ETag returned when that part was uploaded. Keep parts in upload order; the array index determines the part number.

Example Request

curl -X PUT https://poly.cam/api/v1/captures/550e8400-e29b-41d4-a716-446655440000/session.zip \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "sessionZip": {
    "size": 104857600,
    "timestamp": 1706140800000,
    "md5": "abc123..."
  }
}

Error Responses

StatusReason
400Malformed upload bundle
401Missing or invalid API token
403Token does not have the reconstruct scope
404Capture not found, or session.zip has not been uploaded yet

Rate Limits

The API enforces two types of limits on reconstruction jobs to ensure fair usage and system stability:

LimitDescriptionDefault
Daily rate limit Maximum number of reconstruction jobs that can be reserved within a rolling 24-hour window. 25 per 24 hours
Concurrent limit Maximum number of reconstruction jobs that can be active at the same time, including jobs waiting for session.zip upload confirmation. 2 concurrent jobs
Failed jobs count toward the daily limit Jobs that fail still consume capacity and count toward the daily rate limit. They do not count toward the concurrent limit once they have completed (successfully or with an error).

When either limit is exceeded, the Create Capture endpoint returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before retrying.

Get Usage

GET /v1/usage API Token

Returns current API usage and rate limit status for the workspace associated with the API token.

Example Request

curl https://poly.cam/api/v1/usage \
  -H "Authorization: Bearer poly_a1b2c3d4e5f6..."

Response 200

{
  "reconstruct": {
    "used": 6,
    "limit": 25,
    "remaining": 19,
    "windowHours": 24,
    "resetAt": 1706227200000,
    "concurrent": {
      "active": 1,
      "limit": 2,
      "remaining": 1
    }
  }
}

Response Fields

FieldTypeDescription
reconstruct.usednumberNumber of reconstruction jobs started in the current rolling window.
reconstruct.limitnumberMaximum jobs allowed per rolling window.
reconstruct.remainingnumberJobs remaining before hitting the daily limit.
reconstruct.windowHoursnumberRolling window duration in hours.
reconstruct.resetAtnumber?Unix timestamp (ms) when the oldest in-window job expires and a slot opens. Absent when no jobs are in the window.
reconstruct.concurrent.activenumberNumber of currently active reconstruction jobs.
reconstruct.concurrent.limitnumberMaximum concurrent reconstruction jobs allowed.
reconstruct.concurrent.remainingnumberConcurrent slots available.

Error Responses

StatusReason
401Missing or invalid API token

Reconstruction Modes

Pass one of these values as the mode field when you create a capture.

ModeDescription
photoPhotogrammetry reconstruction from image keyframes.
splatGaussian splat reconstruction. Requires Polycam-format frames.json in session.zip.

Tracking Results

Reconstruction runs asynchronously after session.zip is confirmed. Poll the capture metadata endpoint or subscribe to capture webhooks in the Content Management API documentation to track status and inspect generated artifacts.

Health Check

GET /v1/health

Simple health check. No authentication required.

curl https://poly.cam/api/v1/health

Response 200

{
  "status": "ok",
  "service": "api-v1"
}
Polycam Reconstruction API Documentation