Data Feeds let you pull real-time data from your APIs directly into messages at send time. This allows you to deliver highly personalized content without pre-loading data into OneSignal. Use Data Feeds when your data changes frequently, such as:
  • A user’s current rewards balance
  • Latest order status
  • Personalized product recommendations
Other personalization methods (like tags or dynamic content) are great for static data, but Data Feeds are best for live, fast-changing values.
Data Feeds are currently available only for email messages sent through Journeys.
Need another channel? Fill out this short survey.

How Data Feeds Work

  1. Create a Data Feed – Configure how OneSignal connects to your API.
  2. Attach the Data Feed to a message template.
  3. Insert response fields in your message using Liquid syntax.
  4. At send time, OneSignal makes an API call for each recipient, parses the response, and injects the data into your message.

Example: Display reward points

Suppose you want to show each customer their rewards balance:
Hi {{ first_name }},

You have {{ data_feed.rewards.points }} points!
Your membership status is {{ data_feed.rewards.status_level }}.

Keep shopping to earn more points!
When Sarah gets this email, she’ll see her actual points balance and membership status in place of {{ data_feed.rewards.points }} and {{ data_feed.rewards.status_level }}. We’ll use this example to show you how to set up a Data Feed step by step below.

Creating and using a Data Feed

1. Set up your Data Feed configuration

Navigate to Data > Data Feeds in the sidebar to see the list of existing Data Feeds and create a new one. Each Data Feed must have:
  • Name: A descriptive name like “Customer Rewards API” to help you distinguish it in your list of feeds. We recommend these be unique, but it’s not required.
  • Alias: A short name like rewards that you’ll use in Liquid syntax. These must be unique, contain no spaces, and can only contain lower-case alphanumeric characters with no special symbols.
  • Method: How we should contact your API. Usually this is GET but POST is also supported.
  • URL: The address of the API. You can include Liquid syntax, enabling us to call the API to fetch user-specific data.
For example, perhaps your rewards endpoint might be formatted this way, where you would use a data tag to insert the external ID (stored in OneSignal) to fetch that user’s rewards data:
https://acme.com/customers/user_id={{ external_id }}/rewards
  • Headers: Enter header key-value pairs as needed by your API specifications. A typical important use will be to include authentication information. These fields also support Liquid syntax in case it is needed.
A complete Data Feed configuration might look something like this:

Data Feed configuration example

We recommend testing your feed before using it. You test it using Test Subscriptions, so make sure your test subscription attributes will return a real result from your API. Finally, Activate your new Data Feed so it’s ready for use.

2. Attach the Data Feed to your message template

Attach your Data Feed to your message template so that OneSignal knows to use it.
  1. Navigate to Messages > Templates
  2. In the Message section, select the Personalization button

Personalization button options

  1. Toggle on Data Feeds and select your feed

Data Feeds section in the message composer

  1. Save your template

3. Use the data in your message

Use Liquid syntax to insert the response data anywhere in your message. In our example, let’s say that the response for Sarah, whose external_id is 1a1-b2c3, is a simple JSON blob like this:
{
	"external_id": a1-b2c3,
	"points": 193,
	"status_level": "Gold"
}
We want to insert the number of points and the status level into the message, which is accomplished via typical dot notation:
You have {{ data_feed.rewards.points }} points!
Your membership status is {{ data_feed.rewards.status_level }}.
This tells OneSignal:
  • Use a Data Feed
  • Use the rewards Data Feed
    • Recall: the rewards feed knows to call the API with the external_id of the recipient
  • From the response, insert the value of the points item (193) and the status_level item (Gold)

Requirements and Limits

Your API needs to:
  • Accept single-step authentication with auth tokens in headers
  • Respond quickly. Under 250ms recommended (this directly affects send speed)
  • Return JSON. Other formats are not supported at this time.
    • If you have a use case relying on an alternative format, we want to hear from you! Fill out this short survey here.
  • Handle your volume and rate of message sends. If your API has a low rate limit, this will prevent us from delivering your messages quickly.
  • Return reasonably-sized payloads. We recommend keeping responses under 50kb for best performance.
Current limits:
  • One Data Feed per template. We expect to increase this limit in the future. Fill out this short survey here to let us know you need this.
  • One API call per Data Feed per message. Fetch everything you need in one call.
  • Journeys only. Not yet available for other sending methods. Fill out this short survey here to let us know you need this.
  • No chaining calls. The payload from one Data Feed cannot be used to call another.

Setting up your API

Before creating a Data Feed, ensure your API can handle these requirements:

Authentication

Your API should accept authentication via headers:
Authorization: Bearer YOUR_TOKEN
or
X-API-Key: YOUR_KEY

JSON Request Body

If you need to include a body in the request, your API should accept JSON. This may mean your headers need to include Content: application/json.

JSON Response

Your API should return a JSON object. Typically this means your headers will include Accept: application/json.

Personalization Parameters

You’ll typically pass user identifiers in the URL like this:
https://api.example.com/users/{{external_id}}/data
https://api.example.com/rewards?email={{email | url_encode}}
And/or in the body:
JSON
{
	"customer_id": "{{ external_id }}",
	"email": "{{ subscription.email }}"
}
Make sure this data will exist in OneSignal (usually as data tags, but other options are available such as custom events payloads).

Rate Limits

Consider your API’s rate limits. If you’re sending to 10,000 users in rapid succession, we’ll make 10,000 API calls. Ensure your API can handle this volume.

Error Handling

If your API returns an error or doesn’t have data for a user, the message won’t be sent to that recipient. Make sure your API returns data for all expected users.

Getting Started Checklist

Before implementing Data Feeds, answer these questions:
  • What data do I want to show in my message? Working backwards from a simple outline with the items to be populated from your API identified will help you organize your thinking.
  • Is this data available via a single API endpoint?
  • How will I authenticate API requests?
  • What identifier or other data item will I use to fetch personalized data?
  • Is that identifier already stored in OneSignal? If not, how will it be populated?
  • Can my API handle the volume of requests I’ll generate?
  • What happens if my API doesn’t have data for a user?

Examples & advanced use cases

Data Feeds can be used with Liquid syntax or in combination with other features in creative ways to produce more complex personalization.

Iterating with loops: abandoned cart

Let’s say you have a Data Feed cart that returns an array of items in the user’s cart, plus the cart total dollar amount:
JSON
{
  "items": [
    {
      "name": "Blue Running Shoes",
      "price": 84.00,
      "image_url": "https://acme.com/blue-running-shoes.png"
    },
    {
      "name": "Protein Bar",
      "price": 5.99,
      "image_url": "https://acme.com/protein-bar.png"
    }
  ],
  "total": 89.99
}
If you want to show each item in the cart, plus the cart total, you can use a for loop in Liquid:
HTML
<ul>
  {% for item in data_feed.cart.items %}
    <li>
      <strong>{{ item.name }}</strong><br>
      ${{ item.price }}<br>
      <img src="{{ item.image_url }}" alt="{{ item.name }}">
    </li>
  {% endfor %}
</ul>

<p>Cart total: ${{ data_feed.cart.total }}</p>

This will result in:
- Blue Running Shoes
- $84.00
- <running shoes image>
- Protein Bar
- $5.99
- <protein bar image>
Cart total: $89.99
If you’re using the email block editor, when inserting this sort of complex Liquid syntax, particularly if you need to include images or links, for best results use the custom HTML block element.

Custom events payload

Continuing the previous abandoned cart example, how might we know how to fetch that particular cart in the first place? One method might be to create a Journey triggered by a cart_abandoned custom event, where the payload includes a cart_id. In this example that event is being sent to OneSignal via API:
curl
curl --request POST \
  --url https://api.onesignal.com/apps/{app_id}/integrations/custom_events \
  --header 'Accept: application/json' \
  --data '{
  "events": [
    {
      "name": "cart_abandoned",
      "external_id": "user_12345",
      "payload": {
        "cart_id": 98765
      }
    }
  ]
}'

Custom event for Journey entry

The user user_12345 enters the journey when this event is fired, then reaches a node sending an email. That email template is set up with the cart Data Feed, where the URL is set to retrieve the contents of a particular cart like so:
https://acme.com/carts/{{ journey.event.cart_abandoned.data.cart_id }}
Thus, when this particular event is ingested and triggers the Journey:
  1. The cart_id value of 98765 will be stored to the Journey
  2. When the email step is reached, the cart Data Feed will reference that cart_id value and use it to call the cart API
  3. The returned JSON payload will be parsed and inserted into the email as in the previous example above

Conditional display: order status

Let’s say you want to include the status of a customer’s order, but only include a tracking number link if the order has been shipped. You can use an if statement to do so:
Your order is {{data_feed.order.status}}!

{% if data_feed.order.tracking_number != empty %}
Track it here: {{data_feed.order.tracking_url}}
{% endif %}
Here, the tracking link will only be displayed if the tracking_number exists.

Automation without personalization

Data Feeds can be used to automatically insert up-to-date information into your messages without necessarily needing to be personalized per recipient. For example, perhaps you insert a banner image at the top of your emails and change it monthly to keep up with holidays and other monthly events. Rather than remembering to upload a new image to OneSignal and changing all your templates each month, you might set up a Data Feed that fetches the current banner image URL from your CMS or other asset-management location. You would set up a banner Data Feed that points to an endpoint without any variables in the URL like so:
https://acme.com/assets/email-banner
Which returns a response with the current banner URL:
JSON
{
	"banner_url": "https://acme.com/assets/email-banner/2025july.png"
}
You’d set your email template to use {{ data_feed.banner.banner_url }} as the image source URL, automating this process going forward.

Troubleshooting

My data isn’t showing

  1. Check that your Data Feed is attached to the template
  2. Verify your Liquid syntax matches your JSON structure exactly
  3. Test your API endpoint manually to ensure it’s returning data
  4. Check that the user has the required data tags (like external_id)

Messages are sending slowly

  1. Check your API response time
  2. Ensure your API can handle concurrent requests

Some recipients aren’t getting messages

  1. Your API might not have data for those users
  2. Check the error log in the Data Feed configuration for 404s or errors
  3. Check your API logs for 404s or errors
  4. Verify those users have the required identifiers in OneSignal