Custom REST source
The Custom REST source lets you connect any REST API to PostHog's Data Warehouse without waiting for a dedicated integration. You configure the connection using the source builder in the PostHog UI – setting the base URL, authentication, pagination, and the streams (endpoints) you want to sync – and PostHog handles the rest.
This is useful when:
- The API you need isn't available as a managed source
- You want to prototype a connection quickly
- You need to pull data from an internal or niche API
Create a custom REST source
- In PostHog, go to the Data pipeline sources tab.
- Click + New source and select Custom REST source.
- Configure your source using the builder (see Configuration reference below):
- Enter the Base URL for the API (e.g.
https://api.example.com/v1) - Select your Authentication type and enter your credentials
- Add any Default headers the API requires
- Add one or more Streams – each stream maps an API endpoint to a table in your Data Warehouse
- Enter the Base URL for the API (e.g.
- Click Next. PostHog validates the configuration, checks that the URLs are reachable, and probes the first few endpoints to confirm your credentials work.
- Configure sync settings (sync method, frequency) for each stream, then start syncing.
Configuration reference
Base URL
The root URL that all stream paths are resolved against (e.g. https://api.example.com/v1). On PostHog Cloud, all URLs must use https://.
Authentication
Select an authentication type from the Authentication dropdown.
| Type | Fields | Description |
|---|---|---|
| No auth | – | No authentication. Use for public APIs. |
| Bearer token | Token | Sends an Authorization: Bearer <token> header with every request. |
| API key | Location, Header/parameter name, Key | Sends the key as a header, query parameter, or cookie. |
| HTTP basic auth | Username, Password | Sends HTTP Basic credentials with every request. |
Credentials are stored encrypted and never exposed in API responses.
Default headers
Optional key-value pairs sent with every request. Auth headers are added automatically based on your authentication configuration – you don't need to duplicate them here.
Streams
Each stream maps an API endpoint to a table in your Data Warehouse. You can add up to 50 streams per source.
Click Add stream to add a new stream. Each stream has the following fields:
| Field | Description |
|---|---|
| Stream name | The table name in PostHog. Must be unique within the source. |
| Primary key | Column(s) used for deduplication during incremental sync (e.g. id). Separate multiple keys with commas. |
| Path | URL path appended to the base URL (e.g. /v1/users). |
| HTTP method | GET (default) or POST. |
| Records JSONPath | Path to the array of rows in each API response (e.g. data, items, results.data). |
Pagination
Select a Paginator type for each stream. This tells PostHog how to fetch all pages of data from the endpoint.
| Type | Fields | Description |
|---|---|---|
| Single page | – | No pagination. The endpoint returns all data in one response. |
| JSON body next-URL | Next-URL JSONPath | The response body contains a URL to the next page. |
| Cursor in JSON body | Cursor JSONPath, Cursor query param | The response contains a cursor token passed back as a query parameter. |
| Offset / limit query params | Page size, Offset param, Limit param | Classic offset/limit pagination. |
| Page number query param | Page query param, Initial page | Page-based pagination. |
| Link header (RFC 5988) | rel= key in Link header | Follows the Link header to the next page. |
Incremental sync
Enable Incremental sync on a stream to only fetch new or updated records on subsequent syncs instead of re-fetching everything.
| Field | Description |
|---|---|
| Cursor JSONPath | The field in each record used as the sync cursor (e.g. updated_at). Required when incremental sync is enabled. |
| Cursor query param | Query parameter name to send the cursor value (e.g. since). |
| Cursor type | Type of the cursor field: Datetime (default), Date, Timestamp (epoch), or Integer. |
| Upstream row order | Whether the API returns rows in ascending (oldest first) or descending (newest first) order. Pick Descending when the API returns newest rows first – otherwise a resumed sync may skip rows. |
Example configurations
Paginated API with bearer token
To sync users and orders from an API that uses cursor pagination:
- Set Base URL to
https://api.myservice.com/v2 - Set Authentication to Bearer token and enter your token
- Add a stream named
userswith path/usersand Cursor in JSON body pagination (cursor JSONPath:meta.next_cursor, cursor query param:cursor) - Add a second stream named
orderswith the same pagination. Enable Incremental sync with cursor JSONPathupdated_atand cursor query paramupdated_since
API key in query parameter
To sync contacts from an API that authenticates via a query parameter:
- Set Base URL to
https://data.example.com/api - Set Authentication to API key, location to Query parameter, parameter name to
apikey, and enter your key - Add a stream named
contactswith path/contacts, records JSONPathresults, and Offset / limit query params pagination (page size:200)
POST-based query endpoint
Some APIs require POST requests to query data. The builder supports this:
- Set Base URL to
https://analytics.example.com - Set Authentication to Bearer token and enter your token
- Add a stream named
eventswith path/query/eventsand HTTP method POST - Set the paginator to JSON body next-URL with next-URL JSONPath
next_page_url
Limitations
- Maximum 5 custom sources per team
- Maximum 50 streams per source
- Only
GETandPOSTHTTP methods are supported - All URLs must use
https://on PostHog Cloud - URLs pointing to internal or private network hosts are blocked on PostHog Cloud
Troubleshooting
Credential validation fails at creation
PostHog probes the first few endpoints when you create a source. If the API returns HTTP 401 or 403, check that:
- You selected the correct authentication type
- The credentials you entered are valid and have the required permissions
- For API key auth, the location and parameter name match what the API expects
Other status codes (404, 429, 5xx) don't block source creation – those errors surface on the first sync instead.
Validation errors
Common issues:
- Missing base URL – The base URL is required
- No streams – You need at least one stream
- Duplicate stream names – Each stream name must be unique
- Invalid HTTP method – Only
GETandPOSTare supported
Incremental sync skips rows
If using Ascending upstream row order (the default) and the API doesn't guarantee ascending order by the cursor field, rows can be skipped when a sync is interrupted and resumed. Set Upstream row order to Descending if the API returns newest records first.