Feeds provide a poll-based mechanism to access your events from a query perspective.

After you have stored events to your aggregate you may need to distribute these events to other services, application and users. In order to provide fast writes as well as fast reads, we need to decouple the writing operation from this event distribution. This is where the feeds come in. A feed is a generated sequence of entries that is based on the events that you store in your aggregates. These sequences can be queried to create projections (read-models), perform operations, distribute to other systems or do other custom computations.

Transforming events to feeds

When an event batch is stored (consistently) the feed for the corresponding aggregate type will generate new entries for these events. Subscribers of the feed can be other services in Serialized, such as projections and reactions, but is is also possible for you to subscribe to the feeds directly using the API or on of our client libraries.


Entries in all feeds contain stored events. Since events are immutable by design, the feed entries are also immutable, so you can cache these entries in an application that received them to build fast read models. This is roughly how Projections work.

There is one caveat to the immutability of feed entries, and that is if you delete events from the event store. In this case, subsequent requests to the feed will result is an entry response without the deleted events and in such a case, any cached feed entries should be invalidated.

The sequence number of a feed

Each feed provides a sequence number that is monotonically increasing. By default, each aggregate type provides a feed for all events for that type, and therefore the sequence number will increase for each event that is stored for that particular aggregate type. By contrast, the version of a particular aggregate in the event store is only increased when additional events are stored for that particular aggregate id.

Ordering guarantee

Feeds provide ordering guarantees. The sequence number in a feed is monotonically increasing and never out of order. Clients should however handle gaps in the sequence since it may occur when events are deleted in the event store.

Conditional polling

Feeds can be queried using offset by either time or sequence number. Client applications can poll feeds based on the information they know about. Your client application can store the last handled sequence number locally and start from there when requesting future entries.

If you want to rebuild a local state completely, you can send 0 as the since parameter to your feed and you will get all data since the beginning of the feed.

Long polling

To avoid unnecessary network traffic and requests, our client libraries and APIs support long polling. This means that you can send a request to your feed that will generate a response when there are new entries available in the feed.

If there are no available entries after the provided since parameter, your request will hang synchronously for a configurable waitTime.

Partitioned event feeds

It is possible to divide a feed into partitions on request to enable parallelized consumption. The partitioning will be done on the aggregateId field with a modulo operation.

Using this feature makes it possible to have multiple consumers handling different aggregates from the same type.

The special _all feed

Serialized also provide a special type of feed called the _all feed. This feed provides a global, combined feed for all events and all aggregate types in your project.

More information

To read more about the details of feeds, see our API documentation.