Time Series Post API

Posting time series data points is done via an HTTP API with JSON objects as input and output.

API Endpoint

The API POST endpoint is
      https://api.28times.com/series/self/insert

All requests need an API key to be provided as HTTP header:
      Authorization: Bearer <apikey>

You can get an API key for a time series on the time series page by clicking on the "Create API Key" button. The time series in which you want to insert your data point is identified by the API key, as each time series has its own API key. Please note that you cannot post data points into a derived time series, and you will not be able to generate an API key for a derived time series.

Input

The input is a JSON object in this format

{
  "time": "2021-06-21 16:00:00Z",
  "value": 7.9,
  "conflict": "replace",
  "full": "discard"
}

All fields except value are optional.

  • time: specifies the time of the data point in one of the supported formats. If you omit it, the current time of the API call is used, just like when you provide the special time stamp "now".

  • value: is a numeric value of the time stamp.

  • conflict: defines the conflict resolution strategy. A conflict is the insertion of a data point when there is already a data point for that time stamp. Strategies are

    • replace: replaces the existing value with the one given in the API.
    • keep: keeps the stored value and ignores the value given in the API.
    • error: (this is the default) also keeps the stored value, but treats the API call as error. Use this if you do not expect conflicts to occur, so that you can easily identify problems.

  • full: defines the action taken when the maximum number of data points allowed for the time series would be exceeded with the insertion of the new data point.

    • discard: removes the oldest data point in the time series after inserting the new one, in order to stay within the limit.
    • block: does not insert the time point given in the API in case it is really new, and not just replacing the value of an existing one or being ignored by a keep conflict resolution.
    • error: (this is the default) does the same as block, but generates an error in that case.


As a special shortcut for the input, you can just provide a plain value. The default is used for the missing input fields.

514


is the same as

{
  "value": 514
}


and the same as

{
  "time": "now",
  "value": 514,
  "conflict": "error",
  "full": "error"
}


Output

The output of the call is a JSON object confirming either a success or showing an error.

Success

A successful response looks like this:

{
  "outcome": "success",
  "inserted": [
    {"time": "2021-06-21 16:00:00Z", "value": 7.9}
  ]
}


Possible fields in the response object are:

  • inserted: shows the data point that has been inserted. Data points are always shown as object with time and value properties, where time is in ISO format in UTC.
  • replaced: shows a data point that has been replaced by the newly inserted one. The time stamp is the same as the inserted one in this case, but you can see the old value that is no longer stored.
  • unchanged: shows a data point that you tried to insert, but it was already stored with the same value.
  • blocked shows a data point that you tried to insert, but it has been blocked because there was already a data point with that time stamp.
  • discarded: shows a data point that has been removed from the time series via the full = discard policy. If you use this policy, there will always be a discarded field in the output. An empty array shows that nothing has been discarded.

Please note that while these fields are arrays, so they could technically hold more than one object, they will always contain at most one object in this version of the API.

Error

An error response looks like this:

{
  "outcome": "error",
  "title": "Duplicate data point",
  "detail": "A data point with this time stamp already exists",
  "blocked": [
    {"time": "2023-11-04 11:12:13Z", "value": 16}
  ]
}

An error returns always a title, a detail message, and optionally an object identifying the problem.

These are the error types and their HTTP return codes:

  • 400 Bad Request: Some formatting error in the JSON input.
  • 401 Unauthorized: An invalid or missing API key.
  • 409 Conflict: Inserting the data point was rejected due to an already existing data point or due to the time series limit. The affected data point is returned in the blocked field, so that you can see how your input has been interpreted.
  • 422 Unprocessable Entity: Some invalid parameter found, for example no value or non-numeric value, or invalid time stamp, or invalid conflict resolution policy.
  • 429 Too Many Requests: The number of API requests made per day has exceeded the limit for this time series, see below.
  • 500 Internal Server Error: Something outside your scope went wrong.


Example

An example of a valid API call in curl is

  curl https://api.28times.com/series/self/insert \
       -X POST \
       -H "Authorization: Bearer <api-key>" \
       -H "Content-Type: application/json" \
       -d '{"time": "6/12/2017 6:00pm UTC", "value": "3.4"}'

which would return in case of success:

{
  "outcome": "success",
  "inserted": [
    {"time": "2017-06-12 18:00:00Z", "value": 3.4}
  ]
}


Time Series API Quota

Each time series has a quota of daily API requests, see also pricing. The quota is reset daily at midnight UTC. If the quota is exceeded, an error 429 Too Many Requests is returned, and no data is entered or modified in the time series.

You can get the number of remaining requests at this API GET endpoint:
      https://api.28times.com/series/self/quota

which will return:

{
  "outcome": "success",
  "limit": 30,
  "remaining": 5,
  "reset": "2027-07-21 00:00:00Z"
}


These requests do not count against your quota.