Have been dealing with Snapchat API for the past few days, and boy is that a terrible API to work with…

When querying the Campaign stats endpoint and want to get stats on a day level, you need to specify start_date and end_date. That makes sense. It also specifies dateĀ format should follow ISO 8601. That is the only sane date format in the world, so great!

I could take you through my frustrating journey of trial and error, talk about how stupid the date handling is, but let’s skip that part and get straight to the solution.

Use ISO 8601 date/time format INCLUDING time zone information (and url encode your values)

That format looks like this, with date, time and timezone.


You need to URL encode it though, or else…


The time zone information HAS TO MATCH the time zone of the ad account, NOT the zone you are in!

How do you find that information?

SnapcHAt Business Interface

Using the Snapchat business interface, go to:

Settings > Ad Accounts > Select Ad Account

Using API

List all organizations for an account:


List all ad accounts for an organization:


Convert that timezone value to a UTC offset.

Whether you get it from API or interface, you need to take that timezone string and look it up somewhere to get the UTC offset (-08:00 in this case).

Also be aware that Daylight Saving Time (needs to die in a fire, but that’s for another day) might affect the current timezone! So if you use the right timezone and STILL run into problems, try considering DST.


Requires that you already have a project configured with web client credentials.

In order to get the user to consent (which gives is a code we will later exchange for an access token), we need to hand the user a URL.


What is not so documented here is, that you really want to prompt for consent (in case the user has already authenticated once) and you want to ask for access_type=offline

Hand the url to the user.



This gives you a user code, which we will now exchange for access _token + refresh_token using this:


This gives us a response like this:

    "access_token": "ya29.GluuBvGQ-238S6Y0Dq-FYrgDddddddddddddddddddddFA_iB3iv85_hklCaqT3v9lhwgW-lAVvrWaLJUSOxnkBhCjjBhTZz06qWHsDyOJonaZd",
    "expires_in": 3600,
    "refresh_token": "1/xCT3kKuccccccccccccccccccccccccS158hrpVM",
    "scope": "https://www.googleapis.com/auth/analytics.readonly",
    "token_type": "Bearer"

Voila – we now have an access token and a refresh token!