---
title: "Import a website from its URL into a draft"
method: POST
path: "/api/v1/sites/{slug}/import/url"
tags: ["Import"]
---

# Import a website from its URL into a draft

`POST /api/v1/sites/{slug}/import/url`

## Request

- Base URL: `https://api.dotsy.ai`
- URL: `https://api.dotsy.ai/api/v1/sites/{slug}/import/url`
- Auth: HTTP bearer

Starts an import job that copies a website into a draft site at `{slug}`, creating the site. When you already own `{slug}` the import is refused (409 `slug_taken_self`) unless you send `onSlugTaken: merge`, which adds or updates the pages on that site. The call answers `202` straight away. Poll `GET /api/v1/sites/{slug}/jobs/{id}` with `job.id` until `status` is `succeeded` or `failed`, or watch `GET /api/v1/sites/{slug}/import-status`. Use the `slug` in the response from then on: it can differ from the one you sent.

Send only `sourceUrl` to import the whole site: Dotsy finds the pages from the sitemap and the home page links. Everything lands in the draft and nothing is live until you publish. Guest tokens cannot import; use an account key.

## Path parameters

- `slug` string, required

## Headers

- `X-Dotsy-Workspace` string

## Request body

- ImportSiteRequest: What to import. Only `sourceUrl` is needed: it imports every page Dotsy can discover on that site.
  - `sourceUrl` string, uri, required: The website to import. Without `pages` and `segments`, Dotsy imports the whole site it can discover from here.
  - `mode` 'auto' | 'mirror' | 'render': Default `auto`.
  - `pages` object[]: An explicit page list, taken as given. Omit it (and `segments`) to let Dotsy find the pages from the sitemap and the home page links. Two pages sharing a key collapse into one, which comes back as a warning.
    - `url` string, required: Page URL, absolute or relative to `sourceUrl`.
    - `key` string, required: Content key to store the page under.
    - `mode` 'auto' | 'mirror' | 'render': How a page is captured. `mirror` copies the served HTML, `render` runs the page in a browser first (for sites that build their content with JavaScript), `auto` mirrors each page and switches it to render when it looks like a JavaScript-built shell.
  - `segments` object[]: For a site served from several places: each segment expands into pages through its own sitemap.
    - `pathPrefix` string, required: The part of the site this segment covers.
    - `sitemap` string: Sitemap path, relative to `sourceUrl` (for example `/blog/sitemap.xml`). Without it only the segment's hub page is imported, and a `segment_hub_only` warning says so.
    - `mode` 'auto' | 'mirror' | 'render': How a page is captured. `mirror` copies the served HTML, `render` runs the page in a browser first (for sites that build their content with JavaScript), `auto` mirrors each page and switches it to render when it looks like a JavaScript-built shell.
    - `maxPages` integer: Cap for this segment (default 500). A larger sitemap needs an explicit higher cap.
  - `keyConvention` 'flat' | 'directory': Shape of the content keys. `directory` stores `about/index.html` (static-site output); `flat` (default) stores `about.html`.
  - `onSlugTaken` 'auto-suffix' | 'fail' | 'merge': What to do when `{slug}` is already in use. `auto-suffix` (default): when another account holds it, import into `{slug}-{adjective}-{noun}` instead; when you own it, answer 409 `slug_taken_self`. `fail`: when another account holds it, answer 409 `slug_taken` with suggestions. `merge`: when you own it, add or update the pages on that site; when another account holds it, behaves like `auto-suffix`.
  - `assetMirror` 'full' | 'same-host' | 'off': Which asset URLs are copied into Dotsy. `full` (default): the source's own assets and the cross-origin assets they use (CDN images, fonts, scripts). `same-host`: only assets served from the source host. `off`: none, so asset URLs keep pointing at the source.
  - `assetWarm` 'auto' | 'inline' | 'deferred': When mirrored assets are copied. `inline`: during the import. `deferred`: in a background job afterwards. `auto` (default): inline up to 100 external assets, deferred beyond.
  - `skipHosts` string[]: Asset hosts to leave alone: matching URLs stay pointing at the source.
  - `mirrorHosts` string[]: With `assetMirror: full`, copy cross-origin assets only from hosts that match one of these.

## Response `202`

The import is queued.

- ImportAccepted
  - `id` string, required: The import's workflow id. Poll the job with `job.id` on `GET /api/v1/sites/{slug}/jobs/{id}`. The legacy URL `GET …/import/url/{id}` still answers for this id.
  - `status` 'queued', required
  - `slug` string, required: The slug the site is imported into. It differs from the `{slug}` you sent when `slugAdjusted` is true.
  - `requestedSlug` string, required
  - `slugAdjusted` boolean, required: True when another account held the slug and a suffix was added.
  - `pages` integer, required: Pages queued for capture.
  - `warnings` string[], required: Notes from page discovery: capped sitemaps (`import_discovery_capped`), key collisions, routes the import would shadow.
  - `remainingByPrefix` object: Pages left out by a cap, by path prefix. Present only when a cap cut the discovery short.
  - `assetsHint` string: Where to find the copy progress of mirrored assets. Present when `assetMirror` is `full`.
  - `publicUrl` string, required
  - `liveUrl` string, required
  - `liveReady` false, required: Always false: an import lands in the draft until you publish.
  - `job` object: Poll `GET /api/v1/sites/{slug}/jobs/{id}` with `job.id`. Absent when the server cannot issue job ids.
    - `id` string, required: Public job id. Poll GET /api/v1/sites/{slug}/jobs/{id}.
    - `kind` 'import' | 'content' | 'rename', required
    - `status` 'queued' | 'running' | 'succeeded' | 'failed', required

## Other responses

- `400`: `sourceUrl` is missing (`invalid_import`), or the segments expanded to zero pages (`empty_import`, with `warnings`).
- `401`: Missing or invalid credentials.
- `403`: The role or token cannot import into this site: a guest token (`guest_requires_account`), a viewer (`forbidden_role`), a read-only key (`forbidden_read_only`), a key scoped to other sites, an account that may only keep its claimed site (`guest_only`), or the Free plan's site limit for a new site (`free_site_limit`).
- `409`: The slug cannot be used: `slug_taken`, `slug_taken_self`, `invalid_slug`, `reserved_slug`. Or a structure job is running on the site (`structure_active`).
- `413`: The existing site is at its storage limit (`storage_limit_reached`).
- `429`: Too many imports are running (`import_capacity`). Retry when one finishes.

---

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