Skip to main content

Bulk Operations

Efficiently manage large numbers of DNS records with bulk operations including zone import/export and batch record creation.

Batch Record Creation

Create multiple records in a single API call.

Endpoint

POST /v1/dns/zones/{zone_id}/records/batch

Request

curl -X POST https://api.wayscloud.services/v1/dns/zones/zone_abc123/records/batch \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"records": [
{"type": "A", "name": "www", "value": "192.0.2.1", "ttl": 3600},
{"type": "A", "name": "blog", "value": "192.0.2.2", "ttl": 3600},
{"type": "AAAA", "name": "www", "value": "2001:db8::1", "ttl": 3600},
{"type": "MX", "name": "@", "value": "mail.example.com", "priority": 10, "ttl": 3600},
{"type": "TXT", "name": "@", "value": "v=spf1 include:_spf.google.com ~all", "ttl": 3600}
]
}'

Limits:

  • Maximum 1000 records per request
  • All records must be for the same zone
  • Invalid records will cause entire batch to fail

Response

{
"success": true,
"zone_id": "zone_abc123",
"created_count": 5,
"records": [
{
"record_id": "rec_001",
"type": "A",
"name": "www",
"value": "192.0.2.1",
"ttl": 3600
},
{
"record_id": "rec_002",
"type": "A",
"name": "blog",
"value": "192.0.2.2",
"ttl": 3600
}
// ... additional records
]
}

Zone Import

Import existing DNS zones from BIND zone file format.

Endpoint

POST /v1/dns/zones/{zone_id}/import

Zone File Format

Standard BIND zone file format:

$TTL 3600
$ORIGIN example.com.

@ IN SOA grieg.wayscloud.no. hostmaster.example.com. (
2025110401 ; Serial
7200 ; Refresh
3600 ; Retry
1209600 ; Expire
3600 ) ; Minimum TTL

@ IN NS grieg.wayscloud.no.
@ IN NS lindgren.wayscloud.se.
@ IN A 192.0.2.1
www IN A 192.0.2.1
mail IN A 192.0.2.10
@ IN MX 10 mail.example.com.
@ IN TXT "v=spf1 include:_spf.google.com ~all"

Request

curl -X POST https://api.wayscloud.services/v1/dns/zones/zone_abc123/import \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @zone-file.txt

Options:

# Replace all existing records
curl -X POST "https://api.wayscloud.services/v1/dns/zones/zone_abc123/import?replace=true" \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @zone-file.txt

# Merge with existing records (default)
curl -X POST "https://api.wayscloud.services/v1/dns/zones/zone_abc123/import?replace=false" \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @zone-file.txt

Response

{
"success": true,
"zone_id": "zone_abc123",
"imported_records": 15,
"skipped_records": 2,
"errors": [
{
"line": 12,
"record": "invalid IN A 999.999.999.999",
"error": "Invalid IPv4 address"
}
]
}

Zone Export

Export DNS zone to BIND zone file format.

Endpoint

GET /v1/dns/zones/{zone_id}/export

Request

curl -X GET https://api.wayscloud.services/v1/dns/zones/zone_abc123/export \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
> zone-backup.txt

Response

;; Zone: example.com
;; Exported: 2025-11-04T12:00:00Z
;; Records: 15

$TTL 3600
$ORIGIN example.com.

@ IN SOA grieg.wayscloud.no. hostmaster.example.com. (
2025110401 ; Serial
7200 ; Refresh
3600 ; Retry
1209600 ; Expire
3600 ) ; Minimum TTL

@ IN NS grieg.wayscloud.no.
@ IN NS lindgren.wayscloud.se.
@ IN NS aalto.wayscloud.fi.
@ IN NS bohr.wayscloud.dk.

@ IN A 192.0.2.1
www IN A 192.0.2.1
blog IN A 192.0.2.2
www IN AAAA 2001:db8::1
mail IN A 192.0.2.10

@ IN MX 10 mail.example.com.

@ IN TXT "v=spf1 include:_spf.google.com ~all"
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

Batch Record Updates

Update multiple records at once.

Endpoint

PUT /v1/dns/zones/{zone_id}/records/batch

Request

curl -X PUT https://api.wayscloud.services/v1/dns/zones/zone_abc123/records/batch \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{"record_id": "rec_001", "value": "192.0.2.100", "ttl": 300},
{"record_id": "rec_002", "value": "192.0.2.200", "ttl": 300},
{"record_id": "rec_003", "ttl": 300}
]
}'

Response

{
"success": true,
"updated_count": 3,
"records": [
{
"record_id": "rec_001",
"type": "A",
"name": "www",
"value": "192.0.2.100",
"ttl": 300,
"updated_at": "2025-11-04T12:00:00Z"
}
// ... additional records
]
}

Batch Record Deletion

Delete multiple records at once.

Endpoint

DELETE /v1/dns/zones/{zone_id}/records/batch

Request

curl -X DELETE https://api.wayscloud.services/v1/dns/zones/zone_abc123/records/batch \
-H "Authorization: Bearer wayscloud_dns_prod_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"record_ids": ["rec_001", "rec_002", "rec_003"]
}'

Response

{
"success": true,
"deleted_count": 3,
"record_ids": ["rec_001", "rec_002", "rec_003"]
}

Python Examples

Batch Create Records

import requests

def batch_create_records(api_key, zone_id, records):
"""Create multiple DNS records at once"""
url = f'https://api.wayscloud.services/v1/dns/zones/{zone_id}/records/batch'
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}

response = requests.post(url, json={'records': records}, headers=headers)
response.raise_for_status()
return response.json()

# Usage
api_key = 'wayscloud_dns_prod_YOUR_API_KEY'
zone_id = 'zone_abc123'

records = [
{'type': 'A', 'name': 'www', 'value': '192.0.2.1', 'ttl': 3600},
{'type': 'A', 'name': 'blog', 'value': '192.0.2.2', 'ttl': 3600},
{'type': 'AAAA', 'name': 'www', 'value': '2001:db8::1', 'ttl': 3600},
{'type': 'MX', 'name': '@', 'value': 'mail.example.com', 'priority': 10}
]

result = batch_create_records(api_key, zone_id, records)
print(f"Created {result['created_count']} records")

Import Zone File

def import_zone_file(api_key, zone_id, zone_file_path, replace=False):
"""Import DNS zone from BIND zone file"""
url = f'https://api.wayscloud.services/v1/dns/zones/{zone_id}/import'
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'text/plain'
}
params = {'replace': replace}

with open(zone_file_path, 'r') as f:
zone_data = f.read()

response = requests.post(url, data=zone_data, headers=headers, params=params)
response.raise_for_status()
return response.json()

# Usage
result = import_zone_file(api_key, zone_id, 'zone-file.txt', replace=False)
print(f"Imported {result['imported_records']} records")
if result['errors']:
print(f"Errors: {result['errors']}")

Export Zone

def export_zone(api_key, zone_id, output_file):
"""Export DNS zone to BIND zone file"""
url = f'https://api.wayscloud.services/v1/dns/zones/{zone_id}/export'
headers = {'Authorization': f'Bearer {api_key}'}

response = requests.get(url, headers=headers)
response.raise_for_status()

with open(output_file, 'w') as f:
f.write(response.text)

print(f"Zone exported to {output_file}")

# Usage
export_zone(api_key, zone_id, 'backup-2025-11-04.txt')

Migrate Zone Between Providers

def migrate_zone(api_key, from_zone_id, to_zone_id):
"""Migrate all records from one zone to another"""
# Export from source zone
source_url = f'https://api.wayscloud.services/v1/dns/zones/{from_zone_id}/export'
headers = {'Authorization': f'Bearer {api_key}'}

response = requests.get(source_url, headers=headers)
response.raise_for_status()
zone_data = response.text

# Import to destination zone
dest_url = f'https://api.wayscloud.services/v1/dns/zones/{to_zone_id}/import'
response = requests.post(dest_url, data=zone_data, headers=headers)
response.raise_for_status()

result = response.json()
print(f"Migrated {result['imported_records']} records")
return result

# Usage
migrate_zone(api_key, 'zone_old123', 'zone_new456')

Best Practices

1. Validate Before Import

def validate_zone_file(zone_file_path):
"""Validate zone file before importing"""
with open(zone_file_path, 'r') as f:
lines = f.readlines()

errors = []
for i, line in enumerate(lines, 1):
line = line.strip()
if not line or line.startswith(';'):
continue

# Basic validation
if 'IN' not in line:
errors.append(f"Line {i}: Missing 'IN' class")

return len(errors) == 0, errors

# Usage
valid, errors = validate_zone_file('zone-file.txt')
if valid:
import_zone_file(api_key, zone_id, 'zone-file.txt')
else:
print(f"Validation errors: {errors}")

2. Backup Before Replace

def safe_zone_import(api_key, zone_id, zone_file_path, replace=False):
"""Import zone with automatic backup"""
# Backup existing zone
backup_file = f'backup-{zone_id}-{datetime.now().isoformat()}.txt'
export_zone(api_key, zone_id, backup_file)
print(f"Backup created: {backup_file}")

# Import new zone
try:
result = import_zone_file(api_key, zone_id, zone_file_path, replace)
print(f"Import successful: {result['imported_records']} records")
return result
except Exception as e:
print(f"Import failed: {e}")
print(f"Restore from backup: {backup_file}")
raise

3. Chunk Large Batches

def batch_create_records_chunked(api_key, zone_id, records, chunk_size=100):
"""Create records in chunks to avoid limits"""
created = 0

for i in range(0, len(records), chunk_size):
chunk = records[i:i + chunk_size]
result = batch_create_records(api_key, zone_id, chunk)
created += result['created_count']
print(f"Created {created}/{len(records)} records")
time.sleep(1) # Rate limiting

return created

# Usage
created = batch_create_records_chunked(api_key, zone_id, large_record_list)

4. Regular Backups

#!/bin/bash
# daily-dns-backup.sh

API_KEY="wayscloud_dns_prod_YOUR_API_KEY"
BACKUP_DIR="/backups/dns"
DATE=$(date +%Y-%m-%d)

# Get all zones
ZONES=$(curl -s https://api.wayscloud.services/v1/dns/zones \
-H "Authorization: Bearer $API_KEY" | jq -r '.zones[].zone_id')

# Export each zone
for ZONE_ID in $ZONES; do
curl -s https://api.wayscloud.services/v1/dns/zones/$ZONE_ID/export \
-H "Authorization: Bearer $API_KEY" \
> "$BACKUP_DIR/zone-$ZONE_ID-$DATE.txt"
echo "Backed up $ZONE_ID"
done

# Keep only last 30 days
find $BACKUP_DIR -name "*.txt" -mtime +30 -delete

Error Handling

Error CodeDescriptionResolution
BATCH_TOO_LARGEToo many records in batchSplit into smaller batches (max 1000)
INVALID_ZONE_FILEZone file format errorCheck BIND format syntax
DUPLICATE_RECORDSRecords already existUse replace=true or update instead
QUOTA_EXCEEDEDToo many records for planUpgrade plan or delete unused records

Next Steps