---
title: "Create a 360° virtual tour from 360° photos"
method: POST
path: "/api/create_virtual_tour"
tags: ["Virtual tours"]
---

# Create a 360° virtual tour from 360° photos

`POST /api/create_virtual_tour`

## Request

- Base URL: `https://app.pedra.ai`
- URL: `https://app.pedra.ai/api/create_virtual_tour`
- Auth: none required

Imports the 360° photos into a property (a new one unless propertyId is given), then — asynchronously — names unnamed rooms with AI (free) and places navigation links. Returns immediately with status "processing"; poll /api/get_virtual_tour until status is "ready" or "failed" (~10 s per linked room). Credits depend on `linking`: sequential = max(3, ceil(rooms/3)); smart = by room count (≤6: 5, ≤10: 12, ≤15: 25, ≤20: 50, ≤25: 70, ≤30: 100, ≤35: 130, ≤40: 160); none = free. Charged only when linking starts; failed builds cost nothing. One tour per property. Images stored from URLs count toward the daily upload limit (30/day free, 500/day paid); over it, 429 `upload_limit` and nothing is stored.

## Request body

- object
  - `apiKey` string, required: Account API key, sent in the request body. Get one at https://app.pedra.ai under Settings → API.
  - `scenes` TourSceneInput[]: The 360° photos, in walking order (the first is where the tour opens). Required unless propertyId is given, in which case omitting scenes uses all of the property's 360° photos in upload order.
    - union
      - TourScene: One 360° photo in a tour. Give exactly one of imageUrl or imageId. A plain string is treated as an imageUrl.
        - `imageUrl` string: Public URL or data: URI of an equirectangular 360° photo (2:1, 1.9–2.1 accepted, max 80 MB). Dropbox and Google Drive share links are converted to direct downloads.
        - `imageId` string: A 360° photo already in the property (from /api/list_property_images with type "360").
        - `name` string: Room name. Omit it and AI names the room in the tour's language (free). Names you pass are never changed.
      - string: Shorthand for { imageUrl }.
  - `imageUrls` string[]: Shorthand for scenes: a plain list of 360° photo URLs.
  - `propertyId` string: Build the tour in this property. Required when scenes use imageId. Omit to create a new property named after `name`.
  - `name` string: Tour title (and the new property's name when one is created).
  - `linking` 'sequential' | 'smart' | 'none': sequential: each room linked to the next, both directions (pass rooms in walking order). smart: AI links rooms that visibly connect, up to 40 rooms. none: no navigation points.
  - `language` 'en' | 'es' | 'fr' | 'de' | 'it' | 'pt': Tour page UI language and the language of AI room names. Non-English tours get a /{lang}/virtual-tour/... URL.

## Response `200`

Tour created; building in the background.

- object
  - `tourId` string
  - `propertyId` string
  - `name` string, nullable
  - `status` 'processing' | 'ready' | 'failed'
  - `tourUrl` string: Public tour page. Stable across edits.
  - `embedCode` string: Ready-to-paste <iframe> for the tour.
  - `appUrl` string, nullable: The property in the Pedra app.
  - `shareable` boolean: false on free-plan accounts: the tour builds, but its public link shows an upgrade page.
  - `shareableNote` string: Present when shareable is false.
  - `sceneCount` integer
  - `linkCount` integer
  - `coverImageUrl` string, nullable
  - `createdAt` string, date-time
  - `updatedAt` string, date-time
  - `progress` object: Present while status is processing.
    - `stage` 'queued' | 'importing' | 'naming' | 'linking'
    - `done` integer
    - `total` integer
  - `error` string: Present when status is failed.
  - `lastError` string: Present on a ready tour whose last add_virtual_tour_scenes failed.
  - `failedScenes` object[]
    - `index` integer
    - `imageUrl` string
    - `error` string
  - `scenes` object[]
    - `sceneId` string
    - `name` string, nullable
    - `source` string: The URL, "data: URI", or "property" for imageId scenes.
  - `linking` string
  - `creditsCost` integer
  - `estimatedSeconds` integer
  - `message` string

## Other responses

- `400`: Invalid request — e.g. a photo that isn't a 2:1 360° photo, more than 50 scenes, or an unknown enum value.
- `402`: Not enough credits. Nothing is created or charged.
- `404`: Missing, invalid, or revoked API key, or the propertyId isn't on this account.
- `405`: Wrong HTTP method — all endpoints accept POST (credits also accepts GET).
- `409`: The property already has a virtual tour (its tourId is returned).
- `429`: Daily limit reached. Nothing is stored.
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/pedra/apis/pedra-api.md) · [All operations](https://skmtc.dev/pedra/apis/pedra-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pedra/apis/pedra-api/revisions/87b2e76912c8?raw)
