Table of Contents

DNS Records

Manage DNS records within a Cloudflare zone. The SDK provides complete CRUD operations, batch operations, find-by-name, and import/export capabilities.

Overview

Access DNS operations through cf.Dns:

public class DnsService(ICloudflareApiClient cf)
{
    public async Task<DnsRecord> CreateRecordAsync(string zoneId, string name, string content)
    {
        return await cf.Dns.CreateDnsRecordAsync(zoneId,
            new CreateDnsRecordRequest(DnsRecordType.A, name, content, Proxied: true));
    }
}

Creating Records

A Record

var record = await cf.Dns.CreateDnsRecordAsync(zoneId,
    new CreateDnsRecordRequest(
        Type: DnsRecordType.A,
        Name: "www.example.com",
        Content: "192.0.2.1",
        Proxied: true
    ));

CNAME Record (Convenience Method)

var record = await cf.Dns.CreateCnameRecordAsync(
    zoneId,
    name: "cdn.example.com",
    target: "cdn.provider.com",
    proxied: true
);

MX Record

MX records require a priority:

var record = await cf.Dns.CreateDnsRecordAsync(zoneId,
    new CreateDnsRecordRequest(
        Type: DnsRecordType.MX,
        Name: "example.com",
        Content: "mail.example.com",
        Priority: 10
    ));

TXT Record

var record = await cf.Dns.CreateDnsRecordAsync(zoneId,
    new CreateDnsRecordRequest(
        Type: DnsRecordType.TXT,
        Name: "example.com",
        Content: "v=spf1 include:_spf.google.com ~all"
    ));

Record with Comments and Tags

var record = await cf.Dns.CreateDnsRecordAsync(zoneId,
    new CreateDnsRecordRequest(
        Type: DnsRecordType.A,
        Name: "api.example.com",
        Content: "192.0.2.10",
        Proxied: true,
        Comment: "Primary API server",
        Tags: new[] { "env:production", "service:api" }
    ));

Listing Records

List with Pagination

var page = await cf.Dns.ListDnsRecordsAsync(zoneId, new ListDnsRecordsFilters
{
    Page = 1,
    PerPage = 100
});

Console.WriteLine($"Total records: {page.PageInfo.TotalCount}");

foreach (var record in page.Items)
{
    Console.WriteLine($"{record.Name} ({record.Type}): {record.Content}");
}

List All Records

Automatic pagination:

await foreach (var record in cf.Dns.ListAllDnsRecordsAsync(zoneId))
{
    Console.WriteLine($"{record.Name}: {record.Content}");
}

Filtering Records

// List only A records
var aRecords = await cf.Dns.ListDnsRecordsAsync(zoneId,
    new ListDnsRecordsFilters(Type: DnsRecordType.A));

// List only proxied records
var proxied = await cf.Dns.ListDnsRecordsAsync(zoneId,
    new ListDnsRecordsFilters(Proxied: true));

// Filter by name
var wwwRecords = await cf.Dns.ListDnsRecordsAsync(zoneId,
    new ListDnsRecordsFilters(Name: "www.example.com"));

Getting a Record

By ID

var record = await cf.Dns.GetDnsRecordAsync(zoneId, recordId);
Console.WriteLine($"{record.Name}: {record.Content}");

Find by Name

Find a record by hostname (returns null if not found):

var record = await cf.Dns.FindDnsRecordByNameAsync(zoneId, "www.example.com");

if (record is not null)
{
    Console.WriteLine($"Found: {record.Id}");
}

With type filter for disambiguation:

// When a hostname has both A and AAAA records
var aRecord = await cf.Dns.FindDnsRecordByNameAsync(
    zoneId,
    "www.example.com",
    type: DnsRecordType.A
);

Updating Records

Full Replace (PUT)

Replace all record properties:

var updated = await cf.Dns.UpdateDnsRecordAsync(zoneId, recordId,
    new UpdateDnsRecordRequest(
        Type: DnsRecordType.A,
        Name: "www.example.com",
        Content: "192.0.2.100",  // New IP
        Ttl: 3600,
        Proxied: true
    ));

Partial Update (PATCH)

Update only specific fields:

// Change just the content
var updated = await cf.Dns.PatchDnsRecordAsync(zoneId, recordId,
    new PatchDnsRecordRequest(Content: "192.0.2.100"));

// Change TTL only
await cf.Dns.PatchDnsRecordAsync(zoneId, recordId,
    new PatchDnsRecordRequest(Ttl: 3600));

// Toggle proxied status
await cf.Dns.PatchDnsRecordAsync(zoneId, recordId,
    new PatchDnsRecordRequest(Proxied: false));

// Update multiple fields
await cf.Dns.PatchDnsRecordAsync(zoneId, recordId,
    new PatchDnsRecordRequest(
        Content: "192.0.2.100",
        Comment: "Updated to new server"
    ));

Deleting Records

await cf.Dns.DeleteDnsRecordAsync(zoneId, recordId);

Batch Operations

Perform multiple DNS operations in a single API call:

var batch = new BatchDnsRecordsRequest(
    // Delete old records
    Deletes: new[]
    {
        new BatchDeleteOperation("old-record-id-1"),
        new BatchDeleteOperation("old-record-id-2")
    },

    // Create new records
    Posts: new[]
    {
        new CreateDnsRecordRequest(DnsRecordType.A, "new.example.com", "192.0.2.1"),
        new CreateDnsRecordRequest(DnsRecordType.A, "api.example.com", "192.0.2.2")
    },

    // Update existing records (full replace)
    Puts: new[]
    {
        new BatchPutOperation("existing-id",
            new UpdateDnsRecordRequest(DnsRecordType.A, "www.example.com", "192.0.2.3"))
    },

    // Patch existing records (partial update)
    Patches: new[]
    {
        new BatchPatchOperation("another-id", new PatchDnsRecordRequest(Proxied: true))
    }
);

var result = await cf.Dns.BatchDnsRecordsAsync(zoneId, batch);

Console.WriteLine($"Deleted: {result.Deletes?.Count ?? 0}");
Console.WriteLine($"Created: {result.Posts?.Count ?? 0}");
Console.WriteLine($"Updated: {result.Puts?.Count ?? 0}");
Console.WriteLine($"Patched: {result.Patches?.Count ?? 0}");
Note

Execution order: Deletes โ†’ Patches โ†’ Puts โ†’ Posts. This allows you to delete and recreate records with the same name in one batch.

Import/Export

Export to BIND Format

var bindContent = await cf.Dns.ExportDnsRecordsAsync(zoneId);
await File.WriteAllTextAsync("zone-backup.txt", bindContent);

Import from BIND Format

var bindContent = await File.ReadAllTextAsync("zone.txt");

var result = await cf.Dns.ImportDnsRecordsAsync(zoneId, bindContent, proxied: true);

Console.WriteLine($"Parsed: {result.TotalRecordsParsed}");
Console.WriteLine($"Added: {result.RecordsAdded}");

Models Reference

DnsRecord

Property Type Description
Id string Unique record identifier
Name string Record hostname
Type DnsRecordType Record type (A, AAAA, CNAME, etc.)
Content string Record value
Proxied bool Whether proxied through Cloudflare
Proxiable bool Whether the record can be proxied
Ttl int Time to live (1 = automatic)
Priority int? Priority (MX, SRV records)
Comment string? Optional comment
Tags IReadOnlyList<string>? Tags in "name:value" format
CreatedOn DateTime Creation timestamp
ModifiedOn DateTime Last modification timestamp

DnsRecordType (Extensible Enum)

DnsRecordType is an extensible enum supporting all Cloudflare DNS record types.

Common Record Types

Value Description
A Maps hostname to IPv4 address
AAAA Maps hostname to IPv6 address
CNAME Canonical name (alias to another hostname)
MX Mail exchange server
TXT Text record (SPF, DKIM, DMARC, verification)
NS Nameserver delegation
SOA Start of Authority (zone metadata)
PTR Pointer record (reverse DNS)

Service Record Types

Value Description
SRV Service locator (host and port)
HTTPS HTTPS service binding
SVCB Generic service binding
URI URI mapping
NAPTR Naming authority pointer (ENUM, SIP)

Security Record Types

Value Description
CAA Certificate Authority Authorization
DS Delegation Signer (DNSSEC)
DNSKEY DNSSEC public key
TLSA DANE certificate association
SSHFP SSH public key fingerprint
CERT Certificate storage
SMIMEA S/MIME certificate association
using Cloudflare.NET.Dns.Models;

// Use static properties for IntelliSense
var type = DnsRecordType.A;
var cname = DnsRecordType.CNAME;

// Extensible - custom values work for new record types
DnsRecordType customType = "NEW_TYPE";

See conventions for handling unknown values.

ListDnsRecordsFilters

Property Type Description
Type DnsRecordType? Filter by record type
Name string? Filter by exact hostname
Content string? Filter by content
Proxied bool? Filter by proxied status
Page int? Page number (1-based)
PerPage int? Results per page (max 100)
Order string? Field to order by
Direction ListOrderDirection? Sort direction

Common Patterns

Update or Create Record

public async Task<DnsRecord> UpsertARecordAsync(
    string zoneId,
    string hostname,
    string ipAddress)
{
    var existing = await cf.Dns.FindDnsRecordByNameAsync(zoneId, hostname, DnsRecordType.A);

    if (existing is not null)
    {
        return await cf.Dns.PatchDnsRecordAsync(zoneId, existing.Id,
            new PatchDnsRecordRequest(Content: ipAddress));
    }

    return await cf.Dns.CreateDnsRecordAsync(zoneId,
        new CreateDnsRecordRequest(DnsRecordType.A, hostname, ipAddress, Proxied: true));
}

Bulk Update IP Address

public async Task UpdateAllARecordsAsync(string zoneId, string oldIp, string newIp)
{
    var patches = new List<BatchPatchOperation>();

    await foreach (var record in cf.Dns.ListAllDnsRecordsAsync(zoneId,
        new ListDnsRecordsFilters(Type: DnsRecordType.A, Content: oldIp)))
    {
        patches.Add(new BatchPatchOperation(record.Id,
            new PatchDnsRecordRequest(Content: newIp)));
    }

    if (patches.Count > 0)
    {
        await cf.Dns.BatchDnsRecordsAsync(zoneId, new BatchDnsRecordsRequest(Patches: patches));
        Console.WriteLine($"Updated {patches.Count} records");
    }
}

Delete All Records of Type

public async Task DeleteAllTxtRecordsAsync(string zoneId)
{
    var deletes = new List<BatchDeleteOperation>();

    await foreach (var record in cf.Dns.ListAllDnsRecordsAsync(zoneId,
        new ListDnsRecordsFilters(Type: DnsRecordType.TXT)))
    {
        deletes.Add(new BatchDeleteOperation(record.Id));
    }

    if (deletes.Count > 0)
    {
        await cf.Dns.BatchDnsRecordsAsync(zoneId, new BatchDnsRecordsRequest(Deletes: deletes));
    }
}

Required Permissions

Permission Scope Level
DNS Zone Read (for listing/get)
DNS Zone Write (for create/update/delete)