How to send multiple records to Airtable using C# REST API calls

Background

I’m pretty new to working with APIs and JSON data. I’ve been trying to figure out how to send multiple entries to Airtable at once.

I managed to get a single record working with this approach:

public static async Task SendSingleEntry()
{
    using (var client = new HttpClient())
    {
        using (var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.airtable.com/v0/MYAPPID/Content"))
        {
            req.Headers.TryAddWithoutValidation("Authorization", "Bearer MYTOKEN");
            req.Content = new StringContent("{\"records\": [{  \"fields\": {    \"Link\": \"https://example.com/feed.xml\"  }}]}");
            req.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
            var result = await client.SendAsync(req);
        }
    }
}

The Problem

Now I want to send several rows at once instead of just one. I created some classes to handle the data structure:

using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace MyApp
{
    public class ContentModel
    {
        public class FieldData
        {
            [JsonPropertyName("Link")]
            public string Link { get; set; }
        }

        public class Item
        {
            [JsonPropertyName("fields")]
            public FieldData fields { get; set; }
        }

        public class Container
        {
            [JsonPropertyName("records")]
            public List<Item> records { get; set; }
        }
    }
}

Here’s what the JSON should look like:

{
  "records": [
    {
      "id": "rec123ABC",
      "fields": {
        "Link": "https://example.com/feed1.xml"
      }
    },
    {
      "id": "rec456DEF",
      "fields": {
        "Link": "https://example.com/feed2.xml"
      }
    }
  ]
}

I’m stuck on how to populate this object from my data source (like a grid or list) and then serialize it properly for the API call. Any ideas how to make this work?

Been there with Airtable batch operations. The tricky part is getting your object structure right before serializing.

Here’s how I handle it in production:

public static async Task SendMultipleEntries(List<string> links)
{
    var container = new ContentModel.Container
    {
        records = new List<ContentModel.Item>()
    };
    
    // Build your records list
    foreach (var link in links)
    {
        container.records.Add(new ContentModel.Item
        {
            fields = new ContentModel.FieldData
            {
                Link = link
            }
        });
    }
    
    // Serialize and send
    var json = JsonSerializer.Serialize(container);
    
    using (var client = new HttpClient())
    {
        using (var req = new HttpRequestMessage(HttpMethod.Post, "https://api.airtable.com/v0/MYAPPID/Content"))
        {
            req.Headers.TryAddWithoutValidation("Authorization", "Bearer MYTOKEN");
            req.Content = new StringContent(json, Encoding.UTF8, "application/json");
            var result = await client.SendAsync(req);
        }
    }
}

Watch out though - Airtable caps you at 10 records per batch. Got more than that? Chunk your data:

for (int i = 0; i < links.Count; i += 10)
{
    var batch = links.Skip(i).Take(10).ToList();
    await SendMultipleEntries(batch);
}

Skip the “id” field when creating new records. Airtable generates those automatically.

Just wrapped up a similar integration project. You’re missing proper data mapping from your source.

If you’re pulling from a grid or database, here’s what I use:

public static async Task<bool> BulkInsertToAirtable(DataTable sourceData)
{
    var container = new ContentModel.Container { records = new List<ContentModel.Item>() };
    
    foreach (DataRow row in sourceData.Rows)
    {
        container.records.Add(new ContentModel.Item
        {
            fields = new ContentModel.FieldData
            {
                Link = row["LinkColumn"].ToString() // adjust column name
            }
        });
    }
    
    var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
    var jsonPayload = JsonSerializer.Serialize(container, options);
    
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", "Bearer MYTOKEN");
    
    var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
    var response = await client.PostAsync("https://api.airtable.com/v0/MYAPPID/Content", content);
    
    return response.IsSuccessStatusCode;
}

Drop the “id” field from your JSON - that’s only for updates, not inserts.

Heads up: Airtable sometimes accepts your request but still fails individual records. Always check the response body or you’ll miss these failures.

Your model classes look good, but here’s a cleaner way to build the payload without all that manual object construction:

var payload = new {
    records = linksList.Select(link => new {
        fields = new { Link = link }
    }).ToArray()
};

var json = JsonSerializer.Serialize(payload);

This saves you from creating intermediate objects and keeps the code cleaner. One heads up though - I’ve run into cases where Airtable accepts the request but some records still fail validation. Don’t just check the status code, always look at the response content for partial failures.

Had this exact problem last month. Error handling with batches was what got me. Your model looks good, but you need to handle the async stuff properly. I wrapped the serialization in try-catch since JsonSerializer throws exceptions when it hits unexpected nulls. Also, dispose your HttpClient correctly or just use IHttpClientFactory with dependency injection if you’re making lots of calls. For populating from your data source - if it’s a DataGridView or whatever, just loop through the rows and map each one to your Item object. Field names have to match Airtable’s format exactly, including case. I wasted hours on a simple casing issue. Oh, and Airtable’s API gets cranky with rate limits, so add retry logic with exponential backoff if you’re processing big datasets.

Handle the response properly - Airtable returns error details when batch operations fail. Double-check your content-type header too. I’ve seen it get dropped during serialization and cause weird issues. Your model structure looks good though, should work once you populate it right.