Contentstack Delivery .Net SDK | V3
Overview
This reference covers the public API of the contentstack.csharp package, with one page per class. Use it to look up method signatures, parameters, return types, validation behavior, and worked examples for every object you reach from a ContentstackClient.
Before you begin This reference assumes you have installed and initialized the SDK. If you have not, complete the .NET Delivery SDK Get Started guide first. This SDK requires .NET 10 or later, because the package targets net10.0. This reference documents version 3.2.0, and the changelog lists behavior changes between releases.
Minimum Working Example
This snippet creates a client, fetches one entry of a content type, and prints its title.
using System;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
try
{
Entry entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
Console.WriteLine(entry.Title);
}
catch (ContentstackException e)
{
// Thrown when the API rejects the request
Console.WriteLine($"Request failed: {(int)e.StatusCode} {e.ErrorCode} {e.Message}");
}SDK Structure
The SDK uses a builder pattern. ContentstackClient stores your credentials and settings, and each call from it returns an object for a narrower scope, such as a content type, an entry, or a query. A terminal call sends the request: Fetch for a single item, Find or FetchAll for a list, Count for a total, and the Sync methods for bulk retrieval. Every chain starts at the ContentstackClient.
Because option methods store a value on the object and send nothing, an invalid argument fails on the terminal call, which throws a ContentstackException. An option method that checks its own input throws at once instead, for example on a second call that writes the same parameter. Each method page lists the exceptions it throws.
Every terminal call is async and returns a Task, so await it. The generic calls, Fetch<T> and Find<T>, bind the response to the model class you pass. Pass Entry to read fields with Get instead.
Class Overview
Set up the client.
| Class | Role | Accessed via |
|---|---|---|
| ContentstackClient | One stack and environment, and the start of every chain. | new ContentstackClient(...) |
| ContentstackOptions | Every client setting in one object, including region, branch, timeout, and proxy. | new ContentstackOptions() |
| ContentstackRegion | The region that hosts the stack. | ContentstackRegion.EU |
| LivePreviewConfig | The live preview settings of a client. | options.LivePreview |
| Endpoint | A static resolver of Contentstack service URLs for a region. | Endpoint.GetContentstackEndpoint(region, service) |
Read content types and entries.
| Class | Role | Accessed via |
|---|---|---|
| ContentType | One content type and its schema. | client.ContentType(uid) |
| Entry | One entry of a content type. | contentType.Entry(uid) |
| Query | A filtered list of the entries of one content type. | contentType.Query() |
Read assets and global fields.
| Class | Role | Accessed via |
|---|---|---|
| Asset | One published asset. | client.Asset(uid) |
| AssetLibrary | The list of published assets in the stack. | client.AssetLibrary() |
| GlobalField | One global field and its schema. | client.GlobalField(uid) |
| GlobalFieldQuery | The list of global fields in the stack. | client.GlobalFieldQuery() |
Read taxonomies.
| Class | Role | Accessed via |
|---|---|---|
| Taxonomy | Published taxonomies, and the entries tagged with their terms. | client.Taxonomies() |
| Term | One published term of a taxonomy. | taxonomy.Term(uid) |
| TermQuery | The list of published terms of one taxonomy. | taxonomy.Terms() |
Sync content, and read results and errors.
| Class | Role | Accessed via |
|---|---|---|
| SyncStack | The result of a sync request, with its tokens. | client.SyncRecursive() |
| SyncType | The event types a sync returns. | SyncType.EntryPublished |
| ContentstackCollection | The items a list request returns, with the total count when you request it. | query.Find<T>() |
| ContentstackException | The exception a failed request throws. | catch (ContentstackException e) |
Task Index
| Task | Start here | Class |
|---|---|---|
| Fetch one entry by UID | Fetch | Entry |
| Filter entries by a field value | Where | Query |
| Page through the entries of a content type | Skip | Query |
| Return referenced entries in full | IncludeReference | Entry |
| Read an entry in another locale | SetLocale | Entry |
| Read an entry variant | Variant | Entry |
| List the published assets in the stack | FetchAll | AssetLibrary |
| Filter entries by a taxonomy term and the terms below it | EqualAndBelow | Taxonomy |
| Read the schema of a content type | Fetch | ContentType |
| Sync all published content, then only what changed | SyncRecursive | ContentstackClient |
| Show draft content in live preview | LivePreviewQueryAsync | ContentstackClient |
| Set the region, branch, timeout, or proxy | ContentstackOptions | ContentstackOptions |
Key Usage Patterns
Each example below is a complete C# file with its own using directives and client, so you can run it as a top-level program.
Filter and sort entries into your own model class
using System;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
try
{
// The filter and sort methods store values. Find sends the request.
ContentstackCollection<Article> result = await client.ContentType("<CONTENT_TYPE_UID>")
.Query()
.Exists("url")
.Descending("updated_at")
.Limit(10)
.IncludeCount()
.Find<Article>();
Console.WriteLine($"Showing {result.Items.Count()} of {result.Count}");
foreach (Article article in result)
{
Console.WriteLine($"{article.Title}: {article.Url}");
}
}
catch (ContentstackException e)
{
Console.WriteLine(e.Message);
}
// Property names bind to response fields without regard to case.
public class Article
{
public string Uid { get; set; }
public string Title { get; set; }
public string Url { get; set; }
}Page through every entry of a content type
The SDK sends one request per Find call and does not request further pages. Build a new query for each page, and stop at the Count that IncludeCount adds.
using System;
using System.Collections.Generic;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
const int pageSize = 25;
List<Entry> all = new List<Entry>();
int total = 0;
try
{
do
{
// A new query per page, because Skip and Limit throw on a second call.
ContentstackCollection<Entry> page = await client.ContentType("<CONTENT_TYPE_UID>")
.Query()
.Skip(all.Count)
.Limit(pageSize)
.IncludeCount()
.Find<Entry>();
List<Entry> items = page.Items.ToList();
all.AddRange(items);
total = page.Count;
if (items.Count == 0) break;
} while (all.Count < total);
Console.WriteLine($"Read {all.Count} of {total} entries");
}
catch (ContentstackException e)
{
Console.WriteLine(e.Message);
}Sync all published content, then only what changed
using System;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
try
{
// SyncRecursive reads every page before it returns.
SyncStack initial = await client.SyncRecursive();
Console.WriteLine($"Published items: {initial.Items.Count()}");
// Store SyncToken, then pass it on a later run to receive only the changes.
SyncStack changes = await client.SyncToken(initial.SyncToken);
Console.WriteLine($"Changed since the first sync: {changes.Items.Count()}");
}
catch (ContentstackException e)
{
Console.WriteLine(e.Message);
}Register the client with dependency injection in ASP.NET Core
AddContentstack, from the contentstack.aspnetcore package, reads the ContentstackOptions section of your configuration and registers ContentstackClient as a transient service. In an ASP.NET Core app, pass builder.Configuration, which reads that section from appsettings.json. Each key in the section matches a ContentstackOptions property, for example ApiKey, DeliveryToken, and Environment.
The file below registers the client and a service class that receives it through its constructor.
using System;
using System.Threading.Tasks;
using Contentstack.AspNetCore;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public static class ContentstackSetup
{
// Call from Program.cs: ContentstackSetup.AddServices(builder.Services, builder.Configuration)
public static void AddServices(IServiceCollection services, IConfigurationRoot configuration)
{
services.AddContentstack(configuration);
services.AddTransient<ArticleService>();
}
}
// The container passes a new ContentstackClient built from the bound options.
public class ArticleService
{
private readonly ContentstackClient _client;
public ArticleService(ContentstackClient client)
{
_client = client;
}
public async Task<string> GetTitleAsync(string entryUid)
{
try
{
Entry entry = await _client.ContentType("<CONTENT_TYPE_UID>").Entry(entryUid).Fetch<Entry>();
return entry.Title;
}
catch (ContentstackException e)
{
Console.WriteLine(e.Message);
return null;
}
}
}Retry a rate-limited request with a delay
The SDK sends each request once. Catch a 429 status and send the request again after a delay that grows with each attempt.
using System;
using System.Net;
using System.Threading.Tasks;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
const int maxAttempts = 3;
Entry entry = null;
for (int attempt = 1; attempt <= maxAttempts && entry == null; attempt++)
{
try
{
entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
}
catch (ContentstackException e) when (e.StatusCode == HttpStatusCode.TooManyRequests && attempt < maxAttempts)
{
// Wait one second, then two, before the next attempt.
await Task.Delay(TimeSpan.FromSeconds(attempt));
}
catch (ContentstackException e)
{
Console.WriteLine(e.Message);
break;
}
}
Console.WriteLine(entry?.Title);Tell a network failure from an API rejection
A timeout or DNS failure produces no API error body, so ErrorCode stays 0 and StatusCode reports 500 although no server sent it. Check ErrorCode before you trust StatusCode.
using System;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");
try
{
Entry entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
Console.WriteLine(entry.Title);
}
catch (ContentstackException e) when (e.ErrorCode == 0)
{
// No API error body: a timeout, a DNS failure, or a response the SDK could not process
Console.WriteLine($"Request did not complete: {e.Message}");
}
catch (ContentstackException e)
{
// The API answered and rejected the request
Console.WriteLine($"API error: {(int)e.StatusCode} {e.ErrorCode} {e.Message}");
}SDK-Wide Notes
| Concern | Behavior | Default when unset |
|---|---|---|
| Authentication | The ContentstackClient constructor takes the API key, delivery token, and environment, and every object the client creates sends them. No other method takes credentials. | Not applicable. The constructor does not validate the three values, so a missing or wrong value fails at the first request. |
| Error surfacing | Every request method throws a ContentstackException, or one of its subclasses, when the request fails. Read Message, ErrorCode, StatusCode, and Errors. The ContentstackException class page lists which call throws which subclass. | Not applicable. A timeout or DNS failure throws with StatusCode 500 and ErrorCode 0. |
| Regions and endpoints | Set Region on ContentstackOptions, or pass region to the constructor: US, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, or GCP_EU. The client resolves the delivery host for the region through the Endpoint class, from regions.json. A Host value overrides the region. | ContentstackRegion.US, which resolves to cdn.contentstack.io. |
| Branches | Set Branch on ContentstackOptions. The SDK sends it as the branch header on every request. | No branch header, so the API applies its default. |
| Rate limiting and retry | This SDK does not auto-retry on 429, so implement backoff in your calling code. It also sends a request once when the response is a 5xx status or the request times out. | No retry. |
| Timeouts | Set Timeout on ContentstackOptions, or pass timeout to the constructor, in milliseconds. A request that times out throws a ContentstackException with StatusCode 500 and ErrorCode 0. | 30000 ms. |
| Proxy configuration | Set Proxy on ContentstackOptions, or pass proxy to the constructor. Known issue: Query.Find, Query.FindOne, and Query.Count do not use it. See the Query class page. | null. |
| Request URL encoding | The SDK adds query values to the request URL without URL encoding them. A value that holds & or # changes the request URL, so avoid those characters in filter and parameter values. | Not applicable. The SDK never encodes values. |
| Option methods | Option methods store their value on the object that sends the request, and no method removes a stored option. On most classes, a second call that writes the same parameter throws. Build a new object for each request with different options. | Each new object starts with no options. |
| JSON binding | The SDK parses responses with System.Text.Json. Fetch<T> and Find<T> bind response fields to your model class without regard to case. Add converters to client.SerializerOptions.Converters before the first request. | SerializerOptions with case-insensitive property names and the SDK's own converters. |
| Dependency injection | services.AddContentstack(configuration), from the contentstack.aspnetcore package, binds the ContentstackOptions configuration section and registers ContentstackClient as a transient service. | Not applicable. Without it, create the client with a constructor. |
Warning This SDK requires a delivery token, which the client sends as the access_token header. The constructor does not check the token, so a management token or a preview token in its place fails at the first request with a ContentstackException. Pass a preview token in LivePreviewConfig.PreviewToken instead. Refer to Content Delivery API Errors for the full error code list.
SDK Limitations
| Capability | Supported | Notes / Alternative |
|---|---|---|
| Retry a rate-limited or failed request | No | Catch ContentstackException, check StatusCode, and send the request again after a delay, as in Key Usage Patterns above. |
| Read every page of a list in one call | No | Loop with Skip and Limit on a new query per page, as in Key Usage Patterns above. SyncRecursive is the exception, and reads every page of a sync. |
| Reuse one request object for a request with different options | No | Most option methods throw on a second call. Build a new object from the client for each request. |
| Encode special characters in query values | No | Avoid & and # in filter and parameter values. |