Table of contents

  1. Introduction
  2. Getting Set Up
  3. Hello World: Making your first client-side API request
  4. Example use cases
    4.1 POST a Viewed Product event
    4.2 Implementing customer product review events
    4.3 Adding subscriber to a list
    4.4 Create/Edit customer profile
  5. Common errors
  6. Wrapping up

FAQs

1. Introduction

Before we begin

Klaviyo helps businesses better understand and communicate with their customers. To do that though, we need data.

This guide covers how to send data into Klaviyo through a custom integration using Klaviyo's newest generation of APIs. This information is most important for enterprise clients with their own web servers and for smaller businesses that operate on platforms without a pre-built Klaviyo integration.

If your business operates on a major ecommerce platform (e.g. Shopify, WooCommerce), the task of sending your data into Klaviyo is already handled for you. However, if your business has its own systems outside these platforms, then this guide is for you.

This functionality is called /track and /identify in Klaviyo's legacy APIs and /client in the new generation.

Regardless of what they are called, this type of API request can be copied and pasted directly into your website's frontend. Custom integrations use these endpoints to push event data into Klaviyo based on customers' actions (e.g. when a customer views a product, or adds an item to a cart).

This type of information can be used for a variety of purposes. For example, developers could use this data to trigger high value flows like abandon cart nudges or to update customer profiles.

Who is this guide meant for?

  • Developers interested in setting up a custom integration with Klaviyo
  • Developers interested to learn about Klaviyo's newest generation of APIs

What are client-side API requests?

To understand client-side API calls, we'll first cover API keys for your Klaviyo account.

Klaviyo accounts have two types of API keys:

  1. Public API key
  2. Private API key(s)

You can think of a public API key to your Klaviyo account like a username. It is a unique identifier for your business. It does not need to be a secret, which means that it can exist in your website's frontend.

A private API key, on the other hand, is like a password. It is something that should not exist in your website's code for anyone to be able to inspect and see. For API calls requesting or manipulating important information (e.g. customer personal identifiable information), Klaviyo requires a private API key.

Client-side API calls are a subset of functionality that only require a public API key to authenticate. They are less permissioned, but usually easier to integrate into your systems.

Client-side requests have a limited scope in order to prevent abuse of Klaviyo's APIs. To get information on account metrics or customer profiles, developers need to authenticate with a private key.

See here for more information on managing your account's API keys. If you believe a private API key has ever been accidentally exposed, Klaviyo recommends deleting the key and generating a new one. If you are having issues, please reach out to Klaviyo's account support and we will do our best to help you out.

This guide is best viewed on NBViewer

For the best user experience reading this guide, please view it on NBViewer rather than natively in Github. Interacting with the guide on NBviewer cuts down on 404 errors when clicking internal links and better formats long code printouts.

You can find Klaviyo's API guides on NBViewer here.

2. Getting Set Up

What do I need to run this notebook?

The steps listed above are needed if you want to execute the code snippets shown in this guide. Those who are mainly interested to learn how Klaviyo's APIs work should be able to get the information without needing to run the notebook.

It is not strictly necessary to have a sandbox account with fake customer data in order to run this notebook. However, developers who are testing new functionalities should strongly consider it. Especially for use cases where you upload or change customer data, you want to make sure that you are not negatively impacting the data you have on your customers.

What's an SDK?

If you haven't worked with SDKs before (Software Development Kits), think of an SDK like a small library specific to a set of API endpoints.

Klaviyo's SDKs make working with our APIs easier by providing one-line methods for common tasks (e.g. how to update a customer's profile, change subscription status, etc.).

This walkthrough will make use of the Python language and Klaviyo's Python SDK. Klaviyo has released SDKs in Node, PHP, Python, and Ruby. Developers can find more information about Klaviyo's SDKs here.

If you are using one of those languages, Klaviyo highly recommends that you use our SDKs to make API requests. The reason being that these SDKs will perform a lot of best practices automatically under the hood (e.g. retry logic).

If you want to fully customize every part of your usage of our APIs, you are always free to make the calls directly (e.g. through HTTP requests).

This guide covers both direct API requests and requests sent through Klaviyo's SDKs.

Import SDK Client

This tutorial assumes that Klaviyo's SDK is already set up locally. To check out how this is done, select the SDK version and language you want and follow the instructions given on the SDK github. For reference, you can download the SDK with a pip install call like the one commented out below.

Setting up the SDK locally in your codebase is most important for frequent API users and/or developers setting up a production job. Developers who are just testing Klaviyo's API capabilities can start with direct HTTP requests on Klaviyo's endpoints.

The code below imports the SDK Client along with some other helpful libraries.

In [1]:
# you may need to pip install the SDK for Klaviyo's current generation of APIs
# pip install klaviyo-api
In [2]:
from klaviyo_api import KlaviyoAPI

import json
import requests
In [3]:
# Note: You need to provide an API key to instantiate an SDK Client. Since we are using client-side APIs, 
#       we do not need a private key. Feel free to instantiate with an empty string as shown below.

klaviyo = KlaviyoAPI('', max_delay=60, max_retries=3)

Set Account Public Key

To execute the code in this notebook, you will first have to specify your account's public API key. Your account's public key can be found in account settings.

Again, for developers who are testing new functionalities, you should consider using a sandbox account filled with fake customer data.

In [4]:
# TODO: Replace this with your Account Public Key. This is needed to run code blocks below.
public_key = 'PUBLIC_API_KEY'

3. Hello World: Making your first client-side API request

How should my event data be structured?

Data Type

Using Klaviyo's SDK, event data is passed as a dictionary object. However, if you are using CURL or HTTP to request endpoints directly, then you will need to format payloads as JSON strings (an example is shown below).

You can take a look at the structure that payloads need to follow in Klaviyo's API documentation.

For the time being, the most important attributes to understand are profile and metric name. These are broken down in the sections below.

What is the profile attribute?

An event applies to a single customer profile. Therefore, when uploading an event to Klaviyo, you need to specify the customer profile involved. You do this by providing a unique identifier for the relevant profile.

Developers need to supply this information in the profile field. The field should map to a dictionary

Example valid profile fields:

  • 'profile': {'email': string}
  • 'profile': {'$phone_number': string}
  • 'profile': {'$id': string}

Klaviyo recommends always using email as your profile identifier unless your business only uses Klaviyo for SMS marketing. Email is typically a more stable field that will be filled out in almost all of your customer profiles.

Developers who want to use their own id system should write these ids into the external_id field. Customer profiles start with this field blank, but the field can house any string identifier. External ids are managed by you and not Klaviyo, so if you choose to use the field as an identifier there is an additional responsibility to make sure each profile has an id and that they are unique.

What is the Metric Name attribute?

Metrics in your account are essentially event types. For example, a Viewed Product event is an instance of the Viewed Product metric.

When posting an event, developers need to specify what metric the event falls into. Developers do this with the following:

  • 'metric': {'name': string}

If you supply a metric name that already exists in your account, the uploaded event will appear alongside those prior events. If you supply a metric name that does not exist currently in your account (e.g. Hello World), one will be created with that name. Klaviyo accounts are limited to 200 metrics per account.

If you have multiple integrations sending data into your Klaviyo account, then some metric names may appear duplicated. For more information on why this happens, please see below

Hello World: SDK

The code block below shows how to accomplish the same API call as below with a SDK request. It leads to the same results as a HTTP example below.

In [5]:
######## Reminders ########
# Always keep "type" equal to "event"
# time should be formatted as a UTC string
# properties defines the core event attributes. You do not need to provide a schema.

hello_world_payload_as_dict = {'data':
    {
        "type": "event",
        "attributes":
            {
                "profile": {
                    'email': 'julie.rodriguez@klaviyo-demo.com'
                },
                "metric": {
                    "name": 'Hello World'
                },
                "properties": {'Field_1': 'True', 
                               'Field_2': '20', 
                               'Field_3': 'string'
                },
                "time": '2022-09-20T14:33:49+00:00'
            }
    }
}

klaviyo.Client.create_client_event(public_key, hello_world_payload_as_dict)

Hello World: HTTP

The code block below shows how to accomplish the same API call as above with a direct HTTP request. It leads to the same results as a SDK example above.

In [6]:
# The headers for a direct call on the endpoint should look similar to this (with an updated revision date)
headers = {
    'Content-Type': "application/json",
    'revision': "2022-10-17"
}

# move from a dictionary to a json
hello_world_payload_as_json = json.dumps(hello_world_payload_as_dict)

# specify the endpoint you want to hit
url = "https://a.klaviyo.com/client/events/"

# execute the request
response = requests.request("POST", 
                            url, 
                            data=hello_world_payload_as_json, 
                            headers=headers, 
                            params={"company_id": public_key})

print(response)
print(response.reason)
print(response.text)
<Response [202]>
Accepted

After you've run the code block, the event now appears under Julie Rodriguez's profile. The screen below was generated by going into the Profiles tab, then selecting the new Hello World metric under the dropdown menu.

Screen%20Shot%202022-09-19%20at%2012.44.03%20PM.png

4. Example use cases

Now that we have covered client-side events at a high level, it is time to explore specific ways that developers use this functionality.

4.1 POST a Viewed Product event

Why set up a custom integration?

A core task for developers getting set up with Klaviyo is setting up an integration between what is happening on their website and their customer data living in Klaviyo. This is critically important to make sure that events like product orders are quickly and accurately reflected in your Klaviyo data. That type of event data is critical not only for sending follow-up communications, but also to secure future orders by better catering your messaging to your customers' tastes.

If your site is hosted through a major ecommerce platform (e.g. Shopify, WooCommerce, etc.), then this task is already done for you through Klaviyo's pre-built integrations with these platforms. Take a look at Klaviyo's full catalog of integrations.

If you have an online presence outside of those platforms, you may need to set up a custom integration. This is a common task for devs working to integrate larger brands into Klaviyo's ecosystem.

Since this is such a common task for developers, the developer experience team strives to make this as easy as we can. The most common way that devs choose to integrate with Klaviyo is through our JavaScript SDK. This is because of how JS can natively be embedded into websites' frontend.

Why are these examples in Python and not JavaScript?

This guide will cover creating and uploading event data using Python and not JavaScript. This is to make sure that the concepts covered in this guide are accessible to as many developers as possible.

Developers interested in running API requests in other languages should look through Klaviyo's API documentation. It has examples of how to make direct API calls on each of Klaviyo's endpoints in a number of frameworks including (JavaScript, Node, Python, Java, PHP, and Shell among others).

You can also find code snippets using the SDKs in other languages in our SDK documentation. For example, take a look at some common use cases with Klaviyo's JS SDK.

Let's see an example

The code below shows an example of how to define a Viewed Product event. In this case, Julie Rodriguez viewed a copy of Moby dict.

The dictionary describing the event, along with the company's public key, are passed to the create_client_event which uploads the event into the Klaviyo account and links it to Julie's customer profile.

In [7]:
# Note: Make sure uploaded events follow the style covered here: 
#       https://developers.klaviyo.com/en/docs/guide-to-integrating-a-platform-without-a-pre-built-klaviyo-integration

# TODO: replace with a customer email you want this event to apply to. Consider using the sample data tool 
#        to generate some test accounts: https://developers.klaviyo.com/en/docs/generate-sample-data
customer_email = 'julie.rodriguez@klaviyo-demo.com'

payload_as_dict = {'data':
    {
        "type": "event",
        "attributes":
            {
                "profile": {'email': customer_email},
                "metric": {
                    "name": 'Ordered Product'
                },
                "properties": {'Brand': 'Harcourt Classics',
                    'Categories': ['Fiction', 'Classics'],
                    'CompareAtPrice': 19.99,
                    'ImageURL': 'http://www.example.com/path/to/product/image.png',
                    'Price': 19.99,
                    'ProductID': 1112,
                    'ProductName': 'Moby Dict',
                    'URL': 'http://www.example.com/path/to/product'
                },
                "time": '2022-08-17T14:33:49+00:00'
            }
    }
}
      
# Note: on success, the SDK will return None and the HTTP will return a 202
klaviyo.Client.create_client_event(public_key, payload_as_dict)

View in Klaviyo's UI

As we can see, our Ordered Product event for Moby dict is now in Klaviyo. The event will now be reflected in analytics and trigger any appropriate flows.

The screen below was generated by going into the Profiles tab, then selecting the Ordered Product metric under the dropdown menu.

Screen%20Shot%202022-09-19%20at%2012.50.52%20PM.png

Note: Metric names may appear duplicated if the account has multiple integrations.

Developers should be aware that if they upload data into an account from multiple sources, then some metric names may appear more than once in Klaviyo (shown below). This is because Klaviyo separates events based on their source.

So, if an account uploads events both with a custom integration and through a pre-built integration, then the account will appear to have metric duplicates. The test account used in this tutorial has events uploaded through Shopify and Klaviyo's APIs.

Events uploaded from different sources sometimes have different column names or data types. Klaviyo separates these metrics by default to help keep your data clean. Multiple integrations of the same type (e.g. multiple API integrations) will be grouped into the same bucket.

Klaviyo recommends developers keep metric names consistent regardless of the source (e.g. Ordered Product should not become Ordered Product (From Website)).

Klaviyo users of accounts with multiple integrations should be aware that their data is separated by source. Users who aren't aware of this nuance risk incomplete analytics and untriggered flows.

Screen%20Shot%202022-09-19%20at%2012.53.30%20PM.png

4.2 Implementing customer review events

Let's say you are interested in tracking customer feedback on products or services that your business offers. Tracking feedback is a great way to better understand your customers' needs and monitor quality. As a customer data platform, Klaviyo is a natural place for this information to live.

In Klaviyo, you can store reviews, associate them with customer profiles, and implement flows reacting to real-time feedback.

To do this, let us break down this task into a few component parts:

  1. Define the review event
  2. Generate a few test reviews
  3. Upload reviews to Klaviyo
  4. Create a custom flow thanking reviewers

Klaviyo has a flexible, inferred typing model

Before diving into how to implement customer reviews, let's first cover how Klaviyo

One of the most exciting things about working in Klaviyo's data ecosystem is how easy it is for developers to create custom profile attributes or events.

This is because Klaviyo's database uses flexible, inferred typing.

This makes it much easier to POST custom events (as shown below) since developers do not need to provide a schema for new event types or attributes. Rather, Klaviyo handles all that on the back end by implicitly inferring column types from passed parameters.

Schema-less events mean developers have more responsibility to keep types consistent

Developers can create new event types in just a few lines of code with implicit typing. The cost of this ease of development is more responsibility on devs to keep their typing choices consistent.

For example, you could upload a new event in the following (incorrect) format:

# wrong format
"properties": {
    'stringColumn': integer,
    'integerColumn': string
}

Rather than:

# right format
"properties": {
    'stringColumn': string,
    'intColumn': integer
}

Or, developers could mistakenly upload an event misspelling stringColumn as STRINGCOLUMN. These types of errors have the potential to cause problems downstream when pulling summary statistics or triggering flows. So, developers should keep data in a consistent format.

Ultimately though it is up to the developer to keep their company's data in a consistent format.

Klaviyo recommends that developers follow naming conventions suggested in our custom integration guide to keep these fields consistent.

Step 1: Define the review event

Here we can define a structure for the review event. While you do not need to provide a schema to Klaviyo's database, it can be helpful to sketch one out anyway to make sure that the event structure is clear and will be consistent moving forward.

A simple implementation of customer review events needs to track the following:

  • Who wrote the review?
  • When did the review get written?
  • What item was reviewed?
  • What rating did the customer give?
  • What comments (if any) did the customer leave?

The structure of Klaviyo event data already asks for profile and time. The remaining three pieces of information (review_text, review_rating_out_of_5, and catalog_item_id) can be stored in the event's property field.

The structure of this payload should look something like:

event_payload = {'data':
    {
        "type": "event",
        "attributes":
            {
                "profile": {
                    'email': string
                },
                "metric": {
                    "name": string
                },
                "properties": {
                    'review_text': string or NoneType,
                    'review_rating_out_of_5': int,
                    'catalog_item_id': string
                },
                "time": string (UTC-formatted datetime)
            }
    }
}

Step 2: Generate a few test reviews

Now that a structure for this event type has been decided, let us next generate a few fake reviews to test the functionality.

These test reviews (in dictionary format) will form the body of the event data that is uploaded in Step 3.

In [8]:
# Example Review 1

# TODO: replace with a customer email you want this event to apply to. Consider using the sample data tool 
#        to generate some test accounts: https://developers.klaviyo.com/en/docs/generate-sample-data
customer_email = 'stacey.johnson@klaviyo-demo.com'


review_1_body = {
    'data': {
        "type": "event",
        "attributes": 
            {
                "profile": {
                    'email': customer_email
                },
                "metric": {
                    "name": 'Customer Review'
                },
                "properties": {
                    'review_text': 'I loved that chocolate chip cookie I ate!',
                    'review_rating_out_of_5': 5,
                    'catalog_item_id': '$custom:::$default:::SAMPLE-DATA-ITEM-1'
                },
                "time": '2022-08-18T08:10:22+00:00'
            }
    }
}
In [9]:
# Example Review 2

# TODO: replace with a customer email you want this event to apply to.
customer_email = 'julie.rodriguez@klaviyo-demo.com'

review_2_body = {
    'data': {
        "type": "event",
        "attributes": 
            {
                "profile": {
                    'email': customer_email
                },
                "metric": {
                    "name": 'Customer Review'
                },
                "properties": {
                    'review_text': 'My order never arrived 😡',
                    'review_rating_out_of_5': 1,
                    'catalog_item_id': '$custom:::$default:::SAMPLE-DATA-ITEM-1'
                },
                "time": '2022-08-18T08:10:22+00:00'
            }
    }
}
In [10]:
# Example Review 3

# TODO: replace with a customer email you want this event to apply to.
customer_email = 'julie.rodriguez@klaviyo-demo.com'

review_3_body = {
    'data': {
        "type": "event",
        "attributes": 
            {
                "profile": {
                    'email': customer_email
                },
                "metric": {
                    "name": 'Customer Review'
                },
                "properties": {
                    'review_text': None,
                    'review_rating_out_of_5': 4,
                    'catalog_item_id': '$custom:::$default:::SAMPLE-DATA-ITEM-12'
                },
                "time": '2022-08-18T08:10:22+00:00'
            }
    }
}

Step 3: Upload reviews to Klaviyo

Next, we can use the same create_client_event method used in example 4.1.

In [11]:
klaviyo.Client.create_client_event(public_key, review_1_body)
klaviyo.Client.create_client_event(public_key, review_2_body)
klaviyo.Client.create_client_event(public_key, review_3_body)

As we can see, these reviews are now in Klaviyo

There can be a delay of a few minutes for asynchronous, client-side API event requests to show up in Klaviyo. Once the wait is done, however, we can see the review events in Klaviyo.

Going into the Profiles tab, selecting Julie Rodriguez's profile, and choosing the Customer Review metric leads to the following screen. We can see below the two customer reviews associated with Julie's account.

Screen%20Shot%202022-09-19%20at%201.08.16%20PM.png

Step 4: Create a custom flow thanking reviewers

Now that the hard work is done, we can sit and consider what is possible now that customer review data is streaming into your Klaviyo account.

One idea is to set up a custom flow to respond to reviews in real-time.

You can do this through Klaviyo's Flow feature:

Screen%20Shot%202022-09-19%20at%201.18.46%20PM.png

Taking this flow example to the next level, developers can use the custom metadata contained in these review events to target your messaging depending on customers' experience.

For example, a new version of this flow sends a different message depending on whether the review was positive or negative: