# Reporting API V2

## Concepts

This is the new Reporting API. It's designed to be more flexible and easier to work with programmatically.

A few differences with our V1 API:

* All responses are in JSON instead of CSV. This allows for more flexibility and better error handling.
* There are now 2 types of responses: Paginated and Streaming. The latter is more efficient for large results.
* Authentication is only done via JWT tokens. Cookie `adama_session` is no longer supported.
* The HTTP method to perform requests is now `POST` instead of `GET`.
* `fields` is used for both `dimensions` and `metrics`.


### Dataset

*This was known as a Report in the previous version of the API.*

A dataset is a collection of dimensions and metrics that can be queried.

Beta datasets
Datasets marked as **(beta)** are subject to change or removal without prior notice, and are offered as is with no support.

### Date fields

All datetime fields will be in RFC3339 format. For example: `2023-07-27T12:00:00Z`.

Requests must use this format for `start_time` and `end_time`.

When using a `rollup` other than `all`, a field `agg_timestamp` will be included in the response and it will also be
available for ordering. This field will have truncated datetime values based on the `rollup` value.

### Time Windows

Time windows are used to specify a relative period of time to query. The possible values are:

* `current_date`: Today
* `yesterday`: Yesterday
* `last_N_hours`: Last `N` hours (e.g. `last_12_hours`) up to but not including the current hour.
* `last_N_days`: Last `N` days (e.g. `last_7_days`) up to but not including today.
* `campaign_to_date`: From the campaign start date to today.
* `flight_to_date`: From the flight start date to today.


Not all datasets support all time windows.

### Rollups

Rollups are used to aggregate data at different levels. The possible values are:

* `all`: No rollup
* `by_hour`: Rollup by hour
* `by_day`: Rollup by day
* `by_week`: Rollup by week, truncated to the first day of the week
* `by_month`: Rollup by month, truncated to the first day of the month


## Requests

The base URL for the API is https://api.mediamath.com/reporting/v2. Each dataset has its own endpoint, for example:
https://api.mediamath.com/reporting/v2/performance.

All requests must be made using the `POST` method. The body of the request must be a JSON object with the following
schema:


```json
{
  // Array of fields to include in the response
  "fields": [
    "string",
    "..."
  ],
  // Filters to apply to the query
  "filters": [
    {
      "field": "filter1",
      "value": 1
    }
  ],
  // Order to apply to the results
  "order": [
    {
      // Descending order
      "desc": false,
      "field": "agg_timestamp"
    }
  ],
  // Pagination is optional and not all datasets support it
  "pagination": {
    "page_size": 100,
    "page_index": 0
  },
  "rollup": "by_hour",
  "time_window": "last_7_days",
  "start_time": "2023-07-27T00:00:00Z",
  // end_time is optional
  "end_time": "2023-07-28T12:00:00Z"
}
```

`time_window` and `start_time` are required but mutually exclusive. If both are present, `time_window` will be used.

### Filter

A filter is an object with 4 fields:

* `field`: The field to filter by
* `value`: The value to filter by. Ignored if `op` is `and` or `or`.
* `op`: The operator to use for the filter. Defaults to `=`. Possible values:
  * `=`: Equals or contains if `value` is an array. Equivalent to `IN` in SQL.
  * `!=`: Not equals. Equivalent to `NOT IN` in SQL.
  * `>`: Greater than
  * `>=`: Greater than or equals
  * `<`: Less than
  * `<=`: Less than or equals
  * `=~`: Text contains (case-insensitive). Equivalent to `ILIKE` in SQL.
  * `and`: All filters must match
  * `or`: At least one filter must match
  * `isNull`: Field value is null, takes no filter value
  * `isNotNull`: Field value is not null, takes no filter value
* `filters`: An array of filters. Required if `op` is `and` or `or`, ignored otherwise.



```json
{
  "field": "dimension1",
  "op": "=~",
  "value": "Test Campaign"
}
```

#### Complex filters

When using `and` or `or` operators, the `filters` field must be present. Nesting filters is supported.


```json
{
  "field": "dimension1",
  "op": "and",
  "filters": [
    {
      "field": "dimension2",
      "op": ">=",
      "value": 100000
    },
    {
      "field": "dimension3",
      "op": "<",
      "value": 110000
    }
  ]
}
```

## Responses

All examples in these docs are formatted and may include comments. The actual responses won't have indentation and will
always include a JSON object per line. This is an important clarification because the same type of error message is used
for both Paginated and Streaming responses.

### Error responses

When an error occurs, the response will include an error code, a request ID, and a message.

**When reaching out to support, please include the complete response**.


```json
{
  "error_code": "BadRequest",
  "request_id": "74K33s47Yd1ns97E9RZsfFhn1Gj0ehYs",
  "message": "field does not exist in this dataset: campaign_id_test"
}
```

### Paginated response

Paginated responses will include a `meta` object with pagination information and a `results` array with the actual data.
To use pagination, the request must include a `pagination` object with `page_size` and `page_index`.

* `page_size`: The number of records to return per page. The minimum value is `1` and the maximum value is `1000`.
* `page_index`: The index of the page to return. The first page is `0`.



```json
{
  // ...
  "pagination": {
    "page_size": 100,
    "page_index": 0
  },
  // ...
}
```

### Streaming response

The response consists of a header followed by the result-set. In case of failure, a special error message will be
returned and the stream will finish. Also, at any time there can be a **Metadata** message with additional information.

The client is responsible for any validation checks such as checking that `total_items_count` matches the number of
records returned.

Each JSON object in the stream represents a single line of the response. The examples below are formatted for readability.

#### Header


```json
{
  "fields": [
    {
      "name": "agg_timestamp"
    },
    {
      "name": "campaign_id"
    },
    {
      "name": "impressions"
    }
  ],
  // An integer or -1 if the total number of records couldn't be calculated
  "total_items_count": 14
}
```

#### Data


```json
{
  "field1": "value1",
  "field2": 2,
  "agg_timestamp": "2023-07-27T12:00:00Z"
}
```

#### Error

A regular error message will be returned in case of failure. The stream will finish after this message.

#### Metadata

Information such as progress, warnings, or other details that are not part of the result-set.
*At the moment there aren't any metadata messages.*


```json
{
  "meta": {}
}
```

## Examples

### Paginated response

#### Request


```json
{
  "fields": [
    "campaign_id",
    "impressions"
  ],
  "filters": [
    {
      "field": "organization_id",
      "op": "=",
      "value": 123456
    },
    {
      "field": "advertiser_id",
      "value": 123456
    },
    {
      "field": "campaign_name",
      "op": "=~",
      "value": "Test Campaign"
    }
  ],
  "order": [
    {
      "desc": false,
      "field": "agg_timestamp",
      "nulls_last": true
    }
  ],
  "pagination": {
    "page_size": 100,
    "page_index": 0
  },
  "rollup": "by_hour",
  "time_window": "last_7_days"
}
```

#### Response


```json
{
  "meta": {
    "page_index": 0,
    "total_pages": 1,
    "items_count": 3,
    "total_items_count": 3,
    "status": "OK"
  },
  "results": [
    {
      "campaign_id": 1,
      "impressions": 93283,
      "agg_timestamp": "2023-07-27T12:00:00Z"
    },
    {
      "campaign_id": 2,
      "impressions": 474,
      "agg_timestamp": "2023-07-27T12:00:00Z"
    },
    {
      "campaign_id": 3,
      "impressions": 45775,
      "agg_timestamp": "2023-07-27T12:00:00Z"
    }
  ]
}
```

### Streaming response

#### Request


```json
{
  "fields": [
    "campaign_id",
    "impressions"
  ],
  "filters": [
    {
      "field": "organization_id",
      "op": "=",
      "value": 123456
    },
    {
      "field": "advertiser_id",
      "value": 123456
    },
    {
      "field": "campaign_name",
      "op": "=~",
      "value": "Test Campaign"
    }
  ],
  "order": [
    {
      "desc": false,
      "field": "agg_timestamp",
      "nulls_last": true
    }
  ],
  "rollup": "by_hour",
  "time_window": "last_7_days"
}
```

#### Successful response

This is formatted for readability, but the real response will be a single line per object.


```json
{
  "fields": [
    {
      "name": "agg_timestamp"
    },
    {
      "name": "campaign_id"
    },
    {
      "name": "impressions"
    }
  ],
  "total_items_count": 14
}
{
  "campaign_id": 1,
  "impressions": 93283,
  "agg_timestamp": "2023-07-27T12:00:00Z"
}
{
  "campaign_id": 2,
  "impressions": 474,
  "agg_timestamp": "2023-07-27T12:00:00Z"
}
{
  "meta": {}
}
{
  "campaign_id": 3,
  "impressions": 45775,
  "agg_timestamp": "2023-07-27T12:00:00Z"
}
```

### cURL


```bash
curl --location 'https://api.mediamath.com/reporting/v2/performance' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $JWT_TOKEN" \
--data '{
  "fields": [
    "campaign_id",
    "impressions"
  ],
  "filters": [
    {
      "field": "organization_id",
      "op": "=",
      "value": 123456
    },
    {
      "field": "advertiser_id",
      "value": 123456
    },
    {
      "field": "campaign_name",
      "op": "=~",
      "value": "Test Campaign"
    }
  ],
  "order": [
    {
      "desc": false,
      "field": "agg_timestamp",
      "nulls_last": true
    }
  ],
  "rollup": "by_day",
  "time_window": "last_7_days"
}'
```