Documentation

Support

Reporting API Best Practices

Understand the Tapjoy Offerwall Reporting API endpoint, request structure, and error handling to effectively access accurate user acquisition data.
Read time 5 minutesLast updated 3 hours ago

The Reporting API enables advertisers and publishers to access reporting data from the Tapjoy platform and manage their Offerwall content, using the GraphQL query language. For more information on GraphQL, see GraphQL's official website. The API Explorer can be used to assist in query building. Click the book icon on the top left corner to expand the Documentation Explorer and find information on the fields and metrics you can use in your queries.
Documentation explorer panel in the Reporting API Explorer after clicking the book icon

API Endpoint

The Reporting API has a single endpoint:
https://api.tapjoy.com/graphql
The endpoint remains the same regardless of the operation being performed.

Communicating with the API

When you have an access token, requests can be made to the API. To form a request for the API, a JSON-encoded body is provided via an HTTP POST regardless of whether you're executing a query or mutation. Example Request:
POST /graphql
Host: api.tapjoy.com
Authorization: Bearer <OAuth Token>

{
  "query": "query { user { firstName } }"
}

Working with Errors

There are several categories of errors that can be encountered when using the API:
  1. Server / network errors
  2. Authentication / Authorization errors
  3. Query syntax errors
  4. Data validation errors
To ensure that your system is properly integrated, you will need to consider how your system should handle each of these categories of errors. Each category described below will use the following query for context:
query {
  user {
    firstName
    lastName
  }
}

Server / Network Errors

Occasionally you may encounter unrecoverable server / network errors with the API. In these cases, you will receive the proper http status code for the scenario you're encountering. For example, if there is problem in our API preventing queries from executing, then you'll receive an HTTP response with a 500 status code. The best way to handle this scenario is to employ a retry behavior with exponential back-off.

Authentication / Authorization Errors

Authentication / authorization errors can occur for a few reasons:
  • Your OAuth token is expire
  • Your OAuth token is invalid
  • We are incurring issues with our underlying authentication system
In these types of situations, you'll receive a HTTP 200 OK response with the following data:
{
  "errors": [
    {
      "message": "Authentication could not be verified. Please try again later.",
      "code": 403
    }
  ]
}
  • message
    - A human-readable description of the error
  • code
    - A GraphQL status code that corresponds to HTTP status codes
The best way to handle this scenario is to request a new access token and retry the request.

Query Syntax Errors

All queries and mutations are verified upfront that they are syntactically correct and follow the described data schema. If the provided query does not pass all schema validations, then you'll receive a HTTP 200 OK response with the following data:
{
  "errors": [
    {
      "message": "Field 'user' doesn't exist on type 'Query'",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "fields": [
        "query",
        "user"
      ]
    }
  ]
}
  • message
    - A human-readable description of the error
  • locations
    - The locations of the syntax error
  • fields
    - The fields affect by the syntax error
The best way to handle this scenario is to refer to the API documentation and test your query in the Interactive Explorer.

Data Validation Errors

Although you may provide a mutation that is properly constructed with all of the required fields, it can still fail due to business validations. For example, let's say you're an advertiser changing a bid to below the minimum for your AdSet like so:
mutation {
  updateAdSet(input: {
    id: "00000000-0000-0000-0000-000000000000",
    bidding: {amount: 10000}
  }) {
    adSet {
      id
    }
  }
}
In that scenario, you'll receive a HTTP 200 OK response with the following data:
{
  "data": {
    "updateAdSet": null
  },
  "errors": [
    {
      "message": "Amount is below the minimum (20000 micros)",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "updateAdSet",
        "input",
        "bidding",
        "amount"
      ],
      "code": 422
    }
  ]
}
  • message
    - A human-readable description of the error
  • locations
    - The locations of the validation error
  • path
    - A fully-qualified reference to the field that was determined to be invalid
  • code
    - A GraphQL status code that corresponds to HTTP status codes
The best way to handle this scenario is to present the error message alongside the corresponding field (via the path) to the user.

Limitations

The Reporting API has certain protections in place to prevent excessive or abusive API calls. If your integration is exceeding the limits in place, contact your Tapjoy AM or support to understand how your integration can be optimized or how to increase your limits. The examples below are demonstrated in the context of an Advertiser, but would apply to Publisher integrations as well.

Data Retention

The Reporting API can access the last two years of data.

Pagination

When querying for a paginated type, clients must:
  • Supply a
    first
    or
    last
    argument
  • Not exceed 100 nodes in a single page
If the maximum value has been exceeded, then the results will be truncated and, as a result, the requested first / last argument will be ignored. For example:
{
  advertiser {
    adSets(first: 50) {
      edges {
        node {
          id
          name

          ads(first: 50) {
            edges {
              node {
                id
                name
              }
            }
          }
        }
      }
    }
  }
}
In the above query, this will return a maximum of 50 ad sets, each with a maximum of 50 ads. To paginate through collections, you must provide an
after
or
before
argument to control where to start the page. For example:
{
  advertiser {
    adSets(first: 50, after: "Mg==") {
      edges {
        node {
          id
          name
        }
      }

      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}
In the above query, this will return a maximum of 50 ad sets, starting at the cursor position "Mg==". That position is determined based on the endCursor returned from a previous query. Pagination should be considered complete when hasNextPage is false. You can read more about pagination in the GraphQL documentation.

Call Complexity

To pass schema validation, all Marketing API calls must not exceed more than 10,000 "calls". In this case, a "call" refers to a request for a resource. Since GraphQL allows multiple calls to be combined into a single query, the total number of calls for a single query is calculated ahead of time by the API before the query gets executed. The number of calls is roughly equivalent to the number of resources being selected from the result (e.g. campaigns, ad sets, ads, apps, insights, etc.) Let's consider the same example from above:
{
  advertiser {
    adSets(first: 50) {     # <= 50 calls
      edges {
        node {
          id
          name

          ads(first: 50) {  # <= 50 calls
            edges {
              node {
                id
                name
              }
            }
          }
        }
      }
    }
  }
}
In this example, the client is asking for up to 50 ad sets at a time; for each ad set, they are asking for up to 50 ads from the ad set. To calculate the total number of calls:
50      = 50 ad sets
+
50 x 50 = 2500 ads
        = 2550 calls
At the current time, there is no rate limit for number of calls per hour. However, this will likely change in the future.

Insights complexity

Reporting insights being queried at any level incur an additional complexity cost not reflected in the basic calculation above. An additional 1 call is added to the calculation for each day being queried. Let's consider an example:
{
  advertiser {
    # 50 calls
    adSets(first: 50) {
      edges {
        node {
          id
          name

          # 7 calls
          insights(timeRange: {from: "2024-03-01T00:00:00Z", until: "2024-03-08T00:00:00Z"}) {
            timestamps
            reports {
              country
              impressions
              conversions
              spend
            }
          }
        }
      }
    }
  }
}
In this example, the client is asking for up to 50 ad sets at a time; for each ad set, they are asking for 7 days worth of reporting data across 3 metrics and 1 segment. To calculate the total number of calls:
50      = 50 ad sets
+
50 x 7  = 350 insights
        = 400 calls