---
title: Import and Export
path: tutorials/import-export
status: published
---

# Import and Export

Move zones in and out of ScaiDNS using BIND zone files, JSON, or CSV. Useful for migrations, backups, and bulk edits.

**Base path:** `/api/v1/domains/{domain_id}/import` and `/export`
**Required permission:** `records:create` for import, `records:read` for export

## Export

Get a zone as a BIND-format text file:

```bash
curl https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/export \
  -H "X-API-Key: $SCAIDNS_API_KEY"
```

```python
resp = httpx.get(
    f"https://scaidns.scailabs.ai/api/v1/domains/{DOMAIN_ID}/export",
    headers={"X-API-Key": os.environ["SCAIDNS_API_KEY"]},
)
print(resp.json()["zone_file"])
```

```typescript
const resp = await fetch(
  `https://scaidns.scailabs.ai/api/v1/domains/${domainId}/export`,
  { headers: { "X-API-Key": process.env.SCAIDNS_API_KEY! } }
);
const { zone_file } = await resp.json();
console.log(zone_file);
```

Response:

```json
{
  "domain_id": "d_abc123",
  "domain_name": "example.com",
  "format": "bind",
  "zone_file": "$ORIGIN example.com.\n$TTL 3600\n@ SOA ns1.scaidns.scailabs.ai. hostmaster.example.com. (...)\n..."
}
```

### Download as attachment

For saving directly to a file:

```bash
curl https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/export/download \
  -H "X-API-Key: $SCAIDNS_API_KEY" \
  -o example.com.zone
```

Returns the zone file as plain text with `Content-Disposition: attachment; filename="example.com.zone"`.

## Import

Import creates records from a zone file, JSON, or CSV input. Three ways to provide the data:

### Import from pasted text

```bash
curl -X POST https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/import/text \
  -H "X-API-Key: $SCAIDNS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "bind",
    "mode": "merge",
    "content": "www IN A 192.0.2.10\nmail IN A 192.0.2.20"
  }'
```

### Import from file upload

```bash
curl -X POST https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/import \
  -H "X-API-Key: $SCAIDNS_API_KEY" \
  -F "file=@example.com.zone" \
  -F "format=bind" \
  -F "mode=merge"
```

```python
with open("example.com.zone", "rb") as f:
    resp = httpx.post(
        f"https://scaidns.scailabs.ai/api/v1/domains/{DOMAIN_ID}/import",
        headers={"X-API-Key": os.environ["SCAIDNS_API_KEY"]},
        files={"file": f},
        data={"format": "bind", "mode": "merge"},
    )
print(resp.json())
```

```typescript
const form = new FormData();
form.append("file", new Blob([zoneFileText]), "example.com.zone");
form.append("format", "bind");
form.append("mode", "merge");

const resp = await fetch(
  `https://scaidns.scailabs.ai/api/v1/domains/${domainId}/import`,
  {
    method: "POST",
    headers: { "X-API-Key": process.env.SCAIDNS_API_KEY! },
    body: form,
  },
);
```

## Formats

### BIND

Standard zone-file format. Parser handles `$ORIGIN`, `$TTL`, comments, and multi-line records:

```
$ORIGIN example.com.
$TTL 3600
@    IN SOA ns1.scaidns.scailabs.ai. hostmaster.example.com. (
     2026042301 ; serial
     3600       ; refresh
     600        ; retry
     604800     ; expire
     600        ; minimum
     )
@    IN NS   ns1.scaidns.scailabs.ai.
@    IN NS   ns2.scaidns.scailabs.ai.
www  IN A    192.0.2.10
mail IN A    192.0.2.20
@    IN MX   10 mail.example.com.
@    IN TXT  "v=spf1 mx -all"
```

### JSON

Array of record objects:

```json
[
  {"name": "www", "type": "A", "content": "192.0.2.10", "ttl": 300},
  {"name": "mail", "type": "A", "content": "192.0.2.20", "ttl": 300},
  {"name": "@", "type": "MX", "content": "10 mail.example.com.", "ttl": 3600}
]
```

### CSV

Header row required; columns are `name,type,content,ttl,disabled`:

```csv
name,type,content,ttl,disabled
www,A,192.0.2.10,300,false
mail,A,192.0.2.20,300,false
@,MX,10 mail.example.com.,3600,false
```

## Modes

| Mode | Behavior |
|------|---------|
| `replace` | Delete all existing records, then import. Destructive — existing records not in the import are lost |
| `merge` | Add new records; update existing ones where the `(name, type, content)` triple matches |
| `skip_existing` | Add new records only; leave anything that already matches alone |

Default is `merge` if unspecified. Use `replace` with care and only when you're sure the import is complete.

## Preview before importing

See what would happen without applying:

```bash
curl -X POST https://scaidns.scailabs.ai/api/v1/domains/$DOMAIN_ID/import/preview \
  -H "X-API-Key: $SCAIDNS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "bind",
    "mode": "merge",
    "content": "www IN A 192.0.2.10\ninvalid IN BADTYPE ..."
  }'
```

Response:

```json
{
  "records": [
    {"name": "www", "type": "A", "content": "192.0.2.10", "ttl": 3600, "action": "create"}
  ],
  "errors": [
    {"line": 2, "message": "Unknown record type: BADTYPE"}
  ],
  "valid_count": 1,
  "error_count": 1
}
```

Always preview before a `replace` — the preview shows exactly what will change.

## Response

After an actual import:

```json
{
  "imported_count": 42,
  "conflict_count": 3,
  "errors": [
    {"line": 15, "message": "CNAME cannot coexist with A at apex"}
  ]
}
```

- `imported_count` — records successfully created or updated.
- `conflict_count` — records skipped because of existing conflicts (only in `skip_existing` mode).
- `errors` — records that couldn't be imported with the reason.

## Migration pattern

Moving a zone from another provider:

1. Export from the source using that provider's tool (usually produces a BIND file).
2. Create the zone in ScaiDNS (`POST /api/v1/domains/`).
3. Validate it (TXT challenge at the source provider).
4. Preview the import to catch errors: `POST /import/preview` with `mode: merge`.
5. Apply the import: `POST /import/text` or `/import` with `mode: replace` (after the zone's SOA and NS are excluded from the source file, usually automatic).
6. Delegate NS at the registrar to the ScaiDNS nameservers.
7. Wait for TTLs to expire at the old provider.
8. If you want DNSSEC, enable it and publish DS records.

## What's next

- [Managing Records](./managing-records.md) — direct CRUD for records.
- [Bulk Operations](./bulk-operations.md) — record-level bulk create/delete.
- [Templates](./templates.md) — reusable record sets vs ad-hoc imports.
