Base URL
Admin API endpoints are relative to the base API URL:
https://api.behold.so/v1
Authentication
All API requests must be authenticated with an API access token. API access tokens can be generated on the developer settings page of your Behold dashboard. You can have up to two active API access tokens at a time. This is useful for rolling tokens without downtime.
Access tokens should be sent as a Bearer
token in the Authorization
header.
"Authorization: Bearer YOUR_API_ACCESS_TOKEN"
API tokens provide privileged access to your account and should never be used in client-side code or committed to public repositories.
Limitations
- Bulk updates are not supported. You can create or update only one object per request.
- Currently only “grid” widgets are available to create or update via the API.
- Klaviyo widgets cannot be created or updated via API.
Rate limits
Currently the only hard limit on API usage is a cap of 120 requests per minute. If you exceed this you will receive a 429
“Too many requests” error.
In general you should rely on webhooks to monitor your sources and feeds and keep your DB in sync with your Behold account, rather than something like polling.
If you’re regularly bumping into this limit and can’t adjust your request frequency we are happy to set up a custom plan to accommodate. We always reserve the right to deny access to our API for any reason, however, including if we cannot come to an agreement and usage is too high or abusive in some way.
All responses from the API will include the following ratelimit-*
headers that allow you to track and manage your usage.
ratelimit-policy
Describes the rate limit policy that currently applies. In the format 120;w=60
, which describes a policy of 120 allowed requests in a window of 60 seconds.
ratelimit-limit
The maximum number of requests allowed in the given window.
ratelimit-remaining
The number of requests you are still allowed in the current window.
ratelimit-reset
The number of seconds remaining before the rate-limit window resets.
Dates and time
All timestamps are defined in milliseconds since the UNIX epoch. All date and time values are in UTC.
Field filtering
List requests that return multiple objects accept a fields
parameter. This is a comma-separated list of fields to return. The keyword all
may also be used to return all fields.
Some nested map fields have their own fields param. In those cases, the parameter will be named [field]_fields
. E.g. associatedAccounts_fields
.
Field expansion
When retrieving multiple objects via a list query, some fields will return a simple value by default (e.g. a string or integer), but can be expanded to return more details. These fields are marked with the EXPANDABLE
tag. Expansion is accomplished by using the expand
parameter, which accepts a comma separated list of field keys.
At the moment only the views
field on feeds can be expanded. You would accomplish this by adding the query param &expand=views
to your request.
Pagination
Sources and feeds can be fetched in bulk using “list” API endpoints. These endpoints return a cursors
field alongside response data. This field contains two properties: before
and after
. When you first make a call to a list endpoint, if there are more results than can be shown in a single response (defined by the limit
parameter), then cursors
will look like this:
{ "data": [...], "cursors": { "before": null, "after": "bmV4dDpNeSBKU09OIGZlZWQgMjpXWlNpVGh3OWhJUE85R0RKTTN3Nw" }}
This tells you two things:
before
isnull
, so you are at the beginning of the total set of resultsafter
is populated with a cursor ID, so there are more pages of results available
To get the next page of results, make a request with the value of after
in the cursor
param:
curl -G https://api.behold.so/v1/feeds \-H 'Authorization: Bearer YOUR_API_ACCESS_TOKEN' \-d limit=3 \-d cursor='bmV4dDpNeSBKU09OIGZlZWQgMjpXWlNpVGh3OWhJUE85R0RKTTN3Nw'
This will return the next set of results. By using this method, you can traverse every page of results. When after
is null
, you have reached the end and no more results are available.
Likewise, if before
is populated you can move backwards in the set of results by using the before
cursor ID in the cursor
parameter.
Behind the scenes, cursors mark a specific point in a sorted set of results. When moving forwards through results, all results after the cursor are returned. When moving backwards through results, all results before the cursor are returned.
Tip
This type of pagination can produce a slightly unintuitive result in a specific corner case:
If you include a cursor in your request with a before
id, and you also
increase the limit
so that it’s higher than the number of results
available before the cursor, you will see fewer results than the limit
could theoretically allow. This is because you are only shown results in the
dataset before the cursor. The inverse also applies to after
.
Errors
We use standard HTTP response codes to indicate the success or failure of an API request.
Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed because of issues with the request (a required parameter was omitted, a resource wasn’t found, etc.). Codes in the 5xx range indicate an error on our side.
Errors return a JSON object in the response body with the following attributes:
status enum
This will always be error
.
statusCode integer
The HTTP status code.
message string
A message explaining the error.
details map?
Some errors will return a details
field with more specific information about the error.
Example error
{ "status": "error", "statusCode": 400, "message": "Bad request", "details": { "errors": [ { "field": "settings.maxPosts", "message": "Expected number, received string" } ] }}