# Errors

Commerce Layer Metrics API uses [HTTP response codes](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) to show the success or failure of each API request.

* Codes in the `2xx` range indicate success.
* Codes in the `4xx` range indicate an [error](#the-error-object) that failed given the information provided (e.g. due to a bad request, a failed validation, or an expired authentication).
* Codes in the `5xx` range indicate an unhandled error (these are rare and should never happen).

This is the complete list of response codes you can receive when making requests to the Metrics API endpoints:

| Code      |                          | Description                                                                                                                                                                                                                               |
| --------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`200`** | `OK`                     | The request was successfully processed and everything worked as expected.                                                                                                                                                                 |
| **`400`** | `Bad request`            | The request was unacceptable, often due to malformed syntax or sending an unsupported parameter at the first level ([see examples](#400-bad-request)).                                                                                    |
| **`401`** | `Unauthorized`           | The access token was not present in the request, was expired, or didn't have enough permissions ([see example)](#401-unauthorized).                                                                                                       |
| **`404`** | `Not found`              | The requested resource was not found, often due to a typo on the query type or resurce name in the [endpoint](https://docs.commercelayer.io/metrics/api-specification#request).                                                           |
| **`405`** | `Method not allowed`     | The request method is known by the server but has been disabled and cannot be used (Metric API supports `POST` request [only](https://docs.commercelayer.io/metrics/api-specification#request) — [see example](#405-method-not-allowed)). |
| **`406`** | `Not acceptable`         | The **Accept** header was not correctly set to `application/vnd.api.v1+json` (learn more about [versioning](https://docs.commercelayer.io/metrics/api-specification#api-versioning) — [see examples](#406-not-acceptable)).               |
| **`415`** | `Unsupported media type` | The **Content-type** header was not correctly set to `application/vnd.api+json` ([see example](#415-unsupported-media-type)).                                                                                                             |
| **`422`** | `Unprocessable entity`   | The request body was well-formed but contains semantical errors on a nested level, often due to validation constraints ([see examples](#422-unprocessable-entity)).                                                                       |
| **`429`** | `Too many requests`      | The request was not accepted because you hit the [rate limit](#rate-limits).                                                                                                                                                              |
| **`500`** | `Internal server error`  | Something went wrong on our end and is being investigated by our engineers.                                                                                                                                                               |

{% hint style="warning" %}
Correct error handling is important. We recommend writing code that gracefully handles all possible API exceptions.
{% endhint %}

### The error object

All the `4xx` and `5xx` responses include an `error` object in the response body. The object contains the list of errors with some extra information.

<pre class="language-json"><code class="lang-json">{
   "error": {
     "title": "...",
     "code": "...",
     "status": ...,
     "meta": {
<strong>       "errors": [
</strong>         {
           "field_name": [
             {
               "attribute_name": [ "..." ]
             },
             { ... }
           ]
           ...
         }
       ],
       "trace_id": "..."
     }
  }
}
</code></pre>

| Key          | Type    | Description                                                                                                                        |
| ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **`title`**  | String  | Descriptive information about the type of error.                                                                                   |
| **`code`**   | String  | The HTTP response code (e.g. `BAD_REQUEST`, `UNAUTHORIZED`).                                                                       |
| **`status`** | Integer | The HTTP response status (e.g. `400`, `401`).                                                                                      |
| **`meta`**   | Object  | Any [additional information](#undefined) about the errors in the request. Returned for `400`, `404`, `422`, and `500` errors only. |

#### Errors meta

For some types of error the `meta` object is also returned inside the `error` object. It contains extra information that can help you understand what went wrong with your call to the Metrics API and why it wasn't successful:

| Key            | Type             | Description                                                                                                                                                                        |
| -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`errors`**   | Array of objects | The detailed list of errors in the request (with information messages and reference to the related wrong keys). Returned for `400` and `422` errors only.                          |
| **`trace_id`** | String           | The ID of the request (feel free to share it with us in case the information provided isn't enough for you to debug the error). Returned for every type of error except for `401`. |

{% hint style="info" %}
Metrics API query validations are subject to a hierarchy — the [filter](https://docs.commercelayer.io/metrics/api-specification#filter) section is validated before the [query type](https://docs.commercelayer.io/metrics/api-specification#query) one, hence errors in the parameters of the latter won't be thrown unless the ones in the parameters of the former are fixed ([see example](#400-bad-request)).
{% endhint %}

### Rate limits

The requests you perform to the Metrics API are currently subject to the [same rate limits](https://app.gitbook.com/s/-LgByaSP8eKjad-MIuHE/rate-limits#post-and-patch-requests) as the `POST` requests to the Core API, based on the [environment](https://app.gitbook.com/s/-LgByaSP8eKjad-MIuHE/api-specification#environments) and the [limit type](https://app.gitbook.com/s/-LgByaSP8eKjad-MIuHE/rate-limits#other-endpoints):

<table><thead><tr><th>Environment</th><th>Limit type</th><th width="227">Max number of requests</th><th>Time window</th></tr></thead><tbody><tr><td>Live</td><td><em>Average</em></td><td><strong>200</strong> (to all endpoints)</td><td><strong>1 min</strong></td></tr><tr><td>Test</td><td><em>Average</em></td><td><strong>100</strong> (to all endpoints)</td><td><strong>1 min</strong></td></tr><tr><td>Live</td><td><em>Burst</em></td><td><strong>50</strong> (per endpoint)</td><td><strong>10 secs</strong></td></tr><tr><td>Test</td><td><em>Burst</em></td><td><strong>25</strong> (per endpoint)</td><td><strong>10 secs</strong></td></tr></tbody></table>

{% hint style="info" %}
The Core API `/oauth/token` endpoint you need to use for [authentication](https://docs.commercelayer.io/metrics/api-specification#authorization) is subject to [stricter limits](https://app.gitbook.com/s/-LgByaSP8eKjad-MIuHE/rate-limits#authentication-endpoint) (**max 30 reqs / 1 min**).
{% endhint %}

IP addresses that exceed the rates above will be blocked until the frequency of the specific call drops below the allowed limit.

### Examples

#### 400 Bad request

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query but both the query type and the filter keys are empty objects:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/date_breakdown' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
<strong>    "date_breakdown": { },
</strong><strong>    "filter": { }
</strong>  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `400 Bad request` status code but returns only one error (the one about the filter key):

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "filter is not valid",
</strong><strong>    "code": "BAD_REQUEST",
</strong><strong>    "status": 400,
</strong>    "meta": {
      "errors": [
        {
<strong>          "filter": "filter can't be blank if defined"
</strong>        }
      ],
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query with some syntax errors on the first level keys:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/date_breakdown' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
<strong>    "date_breaxdown": {
</strong>      "by": "order.placed_at",
      "field": "order.total_amount_with_taxes",
      "interval": "month",
      "operator": "avg"
    },
<strong>    "filtr": {
</strong>      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "current_date"
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `400 Bad request` status code and return the details about the two syntax errors:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "request is not valid",
</strong><strong>    "code": "BAD_REQUEST",
</strong><strong>    "status": 400,
</strong>    "meta": {
      "errors": [
        {
<strong>          "date_breaxdown": "not valid"
</strong>        },
        {
<strong>          "filtr": "not valid"
</strong>        }
      ],
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### 401 Unauthorized

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using a [sales channel](https://app.gitbook.com/s/-LgByaSP8eKjad-MIuHE/api-credentials#sales-channel) access token:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/search' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
<strong>  -H 'Authorization: Bearer {{your_sales_channel_access_token}}' \
</strong>  -d '{
    "search": {
      "limit": 10,
      "sort": "desc",
      "sort_by": "order.placed_at",
      "fields": [
        "order.id",
        "order.number"
      ]
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "current_date"
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `401 Unauthorized` status code due to failed [authorization](https://docs.commercelayer.io/metrics/api-specification#authorization):

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "the access token you provided is not valid or expired",
</strong><strong>    "code": "UNAUTHORIZED",
</strong><strong>    "status": 401
</strong>  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### 405 Method not allowed

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using the `GET` method:

<pre class="language-shell"><code class="lang-shell"><strong>curl -g -X GET \
</strong>  'https://{{your_domain}}.commercelayer.io/metrics/orders/search' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "search": {
      "limit": 100,
      "sort": "desc",
      "sort_by": "order.created_at",
      "fields": [
        "order.id"
      ]
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "updated_at"
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `405 Method not allowed` status code and returns the related request ID:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "the request method cannot be used, use POST instead",
</strong><strong>    "code": "METHOD_NOT_ALLOWED",
</strong><strong>    "status": 405,
</strong>    "meta": {
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### 406 Not acceptable

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query specifying an unsupported or not existing [API version](https://docs.commercelayer.io/metrics/api-specification#api-versioning) in the header:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/breakdown' \
<strong>  -H 'Accept: application/vnd.api.v3+json' \
</strong>  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "breakdown": {
      "by": "order.country_code",
      "field": "order.id",
      "operator": "value_count",
      "sort": "desc",
      "limit": 20,
      "condition": {
        "gt": 1
      }
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "created_at"
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `406 Not acceptable` status code, suggesting the list of availble versions:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "the API version in the Accept header is not supported, use v1 instead",
</strong><strong>    "code": "NOT_ACCEPTABLE",
</strong><strong>    "status": 406,
</strong>    "meta": {
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using a wrong **Accept** header:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/breakdown' \
<strong>  -H 'Accept: application/javascript' \
</strong>  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "breakdown": {
      "by": "order.country_code",
      "field": "order.id",
      "operator": "value_count",
      "sort": "desc",
      "limit": 20,
      "condition": {
        "gt": 1
      }
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "created_at"
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `406 Not acceptable` status code, suggesting the [correct header format](https://docs.commercelayer.io/metrics/api-specification#http-request-headers):

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "the Accept header was not correctly set to application/vnd.api.v{{version}}+json",
</strong><strong>    "code": "NOT_ACCEPTABLE",
</strong><strong>    "status": 406,
</strong>    "meta": {
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### 415 Unsupported media type

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using a wrong **Content-Type** header:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/stats' \
  -H 'Accept: application/vnd.api.v1+json' \
<strong>  -H 'Content-Type: text/plain' \
</strong>  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "stats": {
      "field": "order.id",
      "operator": "value_count"
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "fulfillment_updated_at"
      },
      "shipping_address": {
        "country_codes": {
          "not_in": [ "IT", "FR", "DE" ]
        }
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `415 Unsupported media type` status code, suggesting the [correct header format](https://docs.commercelayer.io/metrics/api-specification#http-request-headers):

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "the Content-type header was not correctly set to application/vnd.api+json",
</strong><strong>    "code": "UNSUPPORTED_MEDIA_TYPE",
</strong><strong>    "status": 415,
</strong>    "meta": {
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

#### 422 Unprocessable entity

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query specifying only the `date_from` parameter of the filter:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/date_breakdown' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "date_breakdown": {
      "by": "order.placed_at",
      "field": "line_items.discount",
      "interval": "year",
      "operator": "stats"
    },
    "filter": {
      "order": {
<strong>        "date_from": "2021-01-01T00:00:00Z",
</strong><strong>        "date_field": "current_date"
</strong>      },
      "line_items": {
        "discount": {
          "lt": -100
        }
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `422 Unprocessable entity` status code and returns specific information about the wrong key:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "filter is not valid",
</strong><strong>    "code": "UNPROCESSABLE_ENTITY",
</strong><strong>    "status": 422,
</strong>    "meta": {
      "errors": [
        {
          "order": [
          {
<strong>            "date_to": [
</strong><strong>              "if you provide date_from you need to provide also date_to"
</strong><strong>            ]
</strong>          }
          ]
        }
      ],
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using an unsupported operator as the query parameter:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/date_breakdown' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "date_breakdown": {
      "by": "order.placed_at",
      "field": "line_items.discount",
      "interval": "year",
<strong>      "operator": "total"
</strong>    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "current_date"
      },
      "line_items": {
        "discount": {
          "lt": -100
        }
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `422 Unprocessable entity` status code and returns specific information about the wrong key:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "query is not valid",
</strong><strong>    "code": "UNPROCESSABLE_ENTITY",
</strong><strong>    "status": 422,
</strong>    "meta": {
      "errors": [
        {
          "date_breakdown": [
            {
<strong>              "operator": "total it's not a valid value, must be: avg, max, min, sum, stats"
</strong>            }
          ]
        }
      ],
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Request" %}
The following request tries to perform a Metrics API query using some not valid fields and operators as filter parameters:

<pre class="language-shell"><code class="lang-shell">curl -g -X POST \
  'https://{{your_domain}}.commercelayer.io/metrics/orders/date_breakdown' \
  -H 'Accept: application/vnd.api.v1+json' \
  -H 'Content-Type: application/vnd.api+json' \
  -H 'Authorization: Bearer {{your_access_token}}' \
  -d '{
    "date_breakdown": {
      "by": "order.placed_at",
      "field": "line_items.discount",
      "interval": "year",
      "operator": "min"
    },
    "filter": {
      "order": {
        "date_from": "2021-01-01T00:00:00Z",
        "date_to": "2021-12-31T23:59:00Z",
        "date_field": "current_date",
<strong>        "gift_card_code": {
</strong>            "in": [ "GFC00001", "GFC0001"]
        }
      },
      "line_items": {
        "discount": {
          "lt": -100
        },
        "tax_rate": {
<strong>          "in": [ 0.22, 0.1 ]
</strong>        },
        "options": {
<strong>          "name": {
</strong>            "eq": "Engraved"
          }
        }
      }
    }
  }'
</code></pre>

{% endtab %}

{% tab title="Response" %}
The API responds with a `422 Unprocessable entity` status code and returns specific information about the wrong keys:

<pre class="language-json"><code class="lang-json">{
  "error": {
<strong>    "title": "filter is not valid",
</strong><strong>    "code": "UNPROCESSABLE_ENTITY",
</strong><strong>    "status": 422,
</strong>    "meta": {
      "errors": [
        {
          "line_items": [
            {
              "tax_rate": [
                {
<strong>                  "in": "is not a valid operator, please use one from eq, ne, gt, gte, lt, lte, gt_lt, gte_lte, gte_lt, gt_lte"
</strong>                }
              ]
            },
            {
              "options": {
<strong>                "name": [
</strong><strong>                  "not valid"
</strong><strong>                ]
</strong>              }
            }
        ],
        "order": [
            {
<strong>              "gift_card_code": [
</strong><strong>                "not valid"
</strong><strong>              ]
</strong>            }
          ]
        }
      ],
      "trace_id": "1ab23c34-de56-7fab-89cd-e0f1234a1b2c"
    }
  }
}
</code></pre>

{% endtab %}
{% endtabs %}
