---
title: "Create a scan of a space created through the API"
method: POST
path: "/spaces/{id}/scans"
---

# Create a scan of a space created through the API

`POST /spaces/{id}/scans`

## Request

- Base URL: `https://api.spacecontext.app/v1`
- URL: `https://api.spacecontext.app/v1/spaces/{id}/scans`
- Auth: HTTP bearer

Declares what will be uploaded (frames with their timestamps, or one video), the language of the report (`en`, `it`, `es`, `fr`, `de`, `pt`, `nl`; the narration can be in any language) and the optional transcript. The response says how to upload in `upload`; then call `POST /scans/{id}/start`. A live scan needs a card on file, asked here, before any upload link: 402 `PAYMENT_METHOD_REQUIRED` (added in the console, nothing charged), 503 `PAYMENTS_UNAVAILABLE` where payments are not configured; the same rule as the start. A test scan never needs one; its uploads go through the API (a video of at most 95 MiB) and are counted, never stored. The same `Idempotency-Key` returns the same scan. An account creates at most 500 scans a day in each mode (by default, test keys included; a replay does not count): past that, 429 `RATE_LIMIT_EXCEEDED` with `Retry-After` (seconds to midnight UTC). An account holds at most 20 scans created and not started (both modes): past that, 429 `SCANS_IN_PROGRESS` (`max_open_scans`) until some are started or cancelled. A test scan keeps only whether it had a transcript, never its text (its result is the demo). Needs `scans:write`.

## Path parameters

- `id` string, required

## Headers

- `Idempotency-Key` string

## Request body

- ScanCreateRequest
  - `input` object, required
    - `kind` 'frames' | 'video', required
    - `language` 'en' | 'it' | 'es' | 'fr' | 'de' | 'pt' | 'nl': The language the report is written in: area and item names, descriptions, issues, instructions, facts, questions and answers (`en` English, `it` Italian, `es` Spanish, `fr` French, `de` German, `pt` Portuguese, `nl` Dutch). Portuguese is written as in Brazil. The narration can be in any language. Spaces scanned with the iPhone app are in `en` or `it`.
    - `frames` object[]
      - `timestamp_ms` integer, nullable
    - `video` object
      - `content_type` 'video/mp4' | 'video/quicktime' | 'video/x-m4v' | 'video/3gpp' | 'video/webm' | 'video/x-matroska' | 'video/matroska', required: The container of the file: MP4, MOV, M4V or 3GP (H.264, HEVC 8 or 10 bit, AV1, ProRes, MPEG-4 part 2, H.263; AAC, Opus, PCM, AMR or no audio; any frame rate; rotation metadata applied, fragmented MP4 included), WebM or Matroska. A WebM recorded in a browser (MediaRecorder) states no duration: where the API reads videos with its own ffmpeg (`limits.max_video_bytes` of `GET /v1` above 100 MiB) its length is measured and it is read like any other; elsewhere it fails with `VIDEO_UNREADABLE`, not charged (remux it first: `ffmpeg -i in.webm -c copy out.webm`). Any other type is refused here with 400 `INVALID_CONTENT_TYPE`.
      - `content_length` integer, required: The size of the file in bytes. Up to 4294967296 (4 GiB) where the API reads videos with its own ffmpeg ingest, 104857600 (100 MiB) otherwise; `limits.max_video_bytes` of `GET /` says which. A test scan takes at most 99614720 (95 MiB): it uploads through the API. Larger is refused here with 400 `VALIDATION_ERROR`. Above 99614720 (95 MiB) the video can only be sent to the direct upload link (`upload.url`).
    - `transcript` object, nullable
      - `text` string, nullable
      - `segments` object[]
        - `start_ms` integer, required
        - `end_ms` integer, required
        - `text` string, required
    - `recorded_at` string, date-time, nullable
    - `device` string, nullable
    - `duration_seconds` number, nullable

## Response `200`

The scan created with this Idempotency-Key

- Scan
  - `object` 'scan', required
  - `id` string, required
  - `space_id` string, required
  - `livemode` boolean, required
  - `status` 'awaiting_upload' | 'queued' | 'processing' | 'complete' | 'failed' | 'cancelled', required
  - `stage` 'ingesting' | 'screening' | 'observing' | 'fusing' | 'enriching' | 'saving' | 'verifying' | 'null', nullable, required
  - `input` object, required
    - `kind` 'frames' | 'video', required
    - `language` 'en' | 'it' | 'es' | 'fr' | 'de' | 'pt' | 'nl', required: The language the report is written in.
    - `frame_count` integer, nullable, required
    - `video` object, nullable, required
      - `content_type` string
      - `content_length` integer
    - `has_transcript` boolean, required
  - `charge` object, required
    - `kind` 'free_scan' | 'scan_charge' | 'null', nullable, required
    - `price_cents` integer, required
    - `refunded` boolean, required
  - `error` object, nullable, required
    - `code` string
    - `message` string
  - `upload` object, nullable, required
    - `kind` 'frames' | 'video'
    - `method` 'PUT'
    - `url` string
    - `url_template` string
    - `headers` object: Headers the PUT must carry. Content-Length must equal the declared size (HTTP clients set it from the body).
    - `frame_count` integer
    - `max_bytes` integer
    - `content_types` string[]
    - `expires_at` string, date-time: Direct upload links only: the link must be used (the PUT started) within 15 minutes; an upload begun in time completes. Past it, `GET /scans/{id}` (with `scans:write`) gives a fresh link while the scan waits for its upload.
  - `created_at` string, date-time, nullable, required
  - `started_at` string, date-time, nullable, required
  - `completed_at` string, date-time, nullable, required

## Other responses

- `201`: Scan created
- `400`: Error response
- `401`: Error response
- `402`: Error response
- `403`: Error response
- `404`: Error response
- `409`: Error response
- `429`: Too many requests. Retry after the number of seconds in `Retry-After`.
- `503`: Error response

---

[API](https://skmtc.dev/spacecontext/apis/space-context-api.md) · [All operations](https://skmtc.dev/spacecontext/apis/space-context-api/llms.txt) · [OpenAPI document](https://skmtc.dev/spacecontext/apis/space-context-api/revisions/1dca940259b4?raw)
