Area: Optimizely Content Delivery API
Applies to versions: 2 and higher


Recommended reading 

This topic summarizes how the Content Delivery API serializes data returned to clients.

In this topic

How it works

The Content Delivery API defines two concepts, ContentApiModel and PropertyModel, which play a role as data transform objects for the serialization process. Basically, data is transformed to those models before being returned as the request result. The main serialization flow is illustrated by the diagram below.


The workflow should be as follows:

    • You make a request to an endpoint to query data.
    • The request is handled by components (filter, controllers, services, etc) and assumes that the expected data is retrieved in forms of IContent, IEnumerable<IContent>, or Errors.

IContent instances are transformed to ContentApiModel, so this is basically a copy of the Content populated with whatever content interfaces your content implements.

    • The properties of IContent are migrated to ContentApiModel.
    • The PropertyDataCollection of IContent is converted to a list of PropertyModel and then migrated to ContentApiModel
  • ContentApiModel is wrapped in ContentApiResult.Then, the serializer of ContentDeliveryApi serializes everything into JSON and returns it.


This table maps properties between ContentData and ContentApiModel:

ContentApiModel PageData
ContentLinkContentModelReference ContentLink: ContentReference
Name:   string Namestring
ParentLink:  ContentModelReference ParentLinkContentReference
ContentTypeList<string> ContentTypeID: int
LanguageLanguageModel LanguageCultureInfo
ExistingLanguagesList<LanguageModel> ExistingLanguageIEnumerable<CultureInfo>
MasterLanguage: LanguageModel MasterLanguageCultureInfo
RouteSegmentstring URLSegmentstring
Urlstring LinkURLstring
ChangedDateTime? ChangedDateTime
Created DateTime? Created: DateTime
StartPublishDateTime? StartPublishDateTime?
StopPublishDateTime? StopPublish DateTime?
Saved: DateTime? SavedDateTime
Statusstring Status:   VersionStatus
PropertiesIDictionary<string,object> Property:   PropertyDataCollection

Customizing the serialization

If the standard JSON output does not suit your needs, you can customize the serialization to get a different output. You can customize the serialization in a number of ways:

JsonIgnore attribute

If you have ContentData properties that you do not want to be included in the JSON output, just decorate it with the JsonIgnore attribute and it is ignored in the resulting model. The attribute is checked by the ContentModelMapperBase during ContentApiModel building and ignores it if it is present.

		GroupName = SystemTabNames.Content,
		Order = 330)]
	[AllowedTypes(new[] { typeof(IContentData) },new[] { typeof(JumbotronBlock) })]
	public virtual ContentArea RelatedContentArea { get; set; }

Property Model mappers

The Property model is the Content Delivery version of PropertyData, so this is the JSON that is rendered for your content properties.

For example, the PropertyNumber is represented by NumberPropertyModel. The NumberPropertyModel has a Value (the numeric value of property) and a PropertyDataType which usually is the name of the value type (in this case "PropertyNumber").

/// <summary>
/// Mapped property model for <see cref="PropertyNumber"/>
/// </summary>
public class NumberPropertyModel : PropertyModel<int?, PropertyNumber>
    public NumberPropertyModel(PropertyNumber propertyNumber) : base(propertyNumber)
        Value = propertyNumber.Number;

These are the base classes/interfaces for model serialization.


You can use these base classes to implement and register your own custom property models and that way change how custom property types from PageData are serialized. See Custom property models for more information.


This is the most basic Interface that needs to be implemented according to the default PropertyModelConverter (see below). It only contains two properties:

Type Name Description
string Name The name of Content Api Property Model, usually the name of Optimizely Property type
string   PropertyDataType The name of the Optimizely PropertyData


The IPropertyModel interface also needs a constructor with a parameter with the PropertyData. For convenience, the Content Delivery API has a base class for this, that is EPiServer.ContentApi.Core.Serialization.Models.PropertyModel<TValue,TType>, where TValue is the value should be ouputed in JSON and TType is the type of PropertyData.

As the PropertyModel is serialized to JSON, all properties on the implementation are included in the result. The only exception is if you have decorated a property with the JsonIgnore attribute.


If the output of the property depends on personalization, implement the IPersonalizableProperty. The interface only requires a Property called ExcludePersonalizedContent which used to determine if personalized content should be excluded when retrieving content.

public class MyPersonalizedPropertyModel : IPropertyModel, IPersonizableProperty
    public MyPersonalizedPropertyModel(MyPropertyData property, bool excludePersonalizedContent)
        if (excludePersonalizedContent)
            // add logic to exclude personalized content

    public string Name { get; set; }
    public string PropertyDataType { get; set; }
    public bool ExcludePersonalizedContent { get; set; }

By default, property models of ContentArea, ContentReference, ContentReferenceList, and LinkCollection implement this interface. Content Delivery API also has a base class that is EPiServer.ContentApi.Core.Serialization.Models.PersonalizablePropertyModel<TValue, TType>  where TValue is the value that should be outputed in your JSON, and TType is the type of PropertyData.


This interface is used for properties where you can return a simplified set of data for the initial API Request, so that the server can make further requests to the database to dig deeper into the data. By default, the following property types can be expanded: ContentArea, ContentReference, ContentReferenceList, and LinkCollection.

Expand it by adding the parameter ?expand=Steps or ?expand=*. Note that expanding a property only works on the first level of nested content; it does not work on lower levels.


This interface is responsible for mapping data between PropertyData and PropertyModel. The default implementation  DefaultPropertyModelConverter uses reflection to automatically map PropertyData with the corresponding PropertyModel.

Custom property models

If you do not want to use the built-in property models, you can implement and register custom property models. These can change how a property type is serialized and they apply to custom property types of the page data. The custom property models do not affect the internal IContent object properties or the visibility of the property.

To create a custom property model, create a class that inherits from EPiServer.ContentApi.Core.Serialization.Models.PropertyModel and set the Value property in your constructor. It is possible to map your property type to any class, as long as it is serializable and can be properly indexed into Optimizely Search & Navigation (formerly Optimizely Find).

Along with EPiServer.ContentApi.Core.Serialization.Models.PropertyModel, custom property models can also inherit from EPiServer.ContentApi.Core.Serialization.Models.PersonalizablePropertyModel. This class is used when custom property models contain data that is dependent on personalization. It adds an additional constructor parameter, excludePersonalizedContent, which allows you to implement logic in your property model to set the Value property differently in personalized vs. non-personalized contexts.

Registering custom property models

For your custom property models to take effect, you need to create a custom implementation of IPropertyModelConverter. The implementation of IPropertyModelConverter has a collection of EPiServer.ContentApi.Core.Serialization.Models.TypeModel called ModelTypes, which maintains a map of which PropertyData instances that the converter is capable of handling.

The simplest method of adding a custom converter is to extend the default one, DefaultPropertyModelConverter, and override the SortOrder and ModelTypes properties.

Example: The following handler registers the LowercaseLongStringPropertyModel from the above example, with a SortOrder higher than the default handler (SortOrder = 0), ensuring that the custom converter is used for all Long String properties.

[ServiceConfiguration(typeof(IPropertyModelConverter), Lifecycle = ServiceInstanceScope.Singleton)]
public class LowercaseLongStringPropertyModelConverter : DefaultPropertyModelConverter
    public LowercaseLongStringPropertyModelConverter()
        ModelTypes = new List<TypeModel>
            new TypeModel 
                ModelType = typeof(LowercaseLongStringPropertyModel), ModelTypeString = nameof(LowercaseLongStringPropertyModel), PropertyType = typeof(PropertyLongString)
    public override int SortOrder { get; } = 100;

In some cases, a full implementation of IPropertyModelConverter may be preferable, such as when custom logic is required to choose between different PropertyModel implementations. In that case, your custom converter must also implement HasPropertyModelAssociatedWith, which verifies, based on the provided PropertyData, that the implementation of IPropertyModelConverter is able to handle the provided type. In addition, your custom converter must also implement ConvertToPropertyModel method, which creates and returns any instances of your custom property models based on the provided instance of PropertyData.

Working with ContentModelMapper

The IContentModelMapper implementation is where you can customize built-in PageData properties. It gives you full control over ContentApiModel and its properties are converted and mapped. Derive from DefaultContentModelMapper and then override one or more virtual methods of ContentModelMapperBase.

Note that the resulting objects will always be of the ContentApiModel type, so you may end up with a lot of unnecessary data in your resulting output. See Output model filtering.

See the MusicFestival template site for some examples of extending ContentModelMapper.

Output model filtering

By customizing ContentResultService, you can filter out properties from the data returned by Content Delivery API. This is called model filtering and has the benefit of decreasing your models. See How to customize API to change data returned to clients for more information.

Create custom controller

In the case where you have special requirements, you can create your own custom controller, and reuse those for different scenarios.

Related information

Blog posts

    Do you find this information helpful? Please log in to provide feedback.

    Last updated: Jul 02, 2020

    Recommended reading