Event API
The Specops Event API lets you integrate authentication and security events from Specops services into external systems such as SIEM platforms, monitoring tools, and automation workflows.
The API works by polling. Clients request new events at regular intervals, and the API returns them as a time-ordered stream using cursor-based pagination, one page at a time. There are no webhooks, so a typical setup is a small script or service that runs continuously and forwards each event to downstream systems, such as a SIEM.
Each response includes a cursor that records exactly how far you have read. By sending that cursor with the next request, you can continue streaming events forward without refetching or reprocessing events you have already handled.
The API is designed to support continuous ingestion into external systems, not for ad hoc historical searches or occasional compliance lookups. A typical client is expected to:
- fetch historical data on initial startup (up to 7 days back)
- continue polling for new events
- handle retries and pagination
- forward events to downstream systems such as a SIEM
Note
Clients must handle rate limiting, or they may receive HTTP 429 responses. Excessive polling can lead to temporary blocking or other protective rate-limiting measures. See Throttling. We also recommend de-duplicating events returned by the Specops Event API using eventRecordId. See If the cursor is rejected or lost.
Prerequisites
Configuration
The following are required in order to start using the API.
- API Key: From the Admin pages in Specops Authentication Web, generate an API key for the Event API resource. The key is a 72-character string. Store it securely and treat it like a password.
-
Base URL: Use the base URL for the data center your key was issued in.
Data center Base URL EU https://eu.eventapi.specopssoft.comUS https://eventapi.specopssoft.com
API Headers
The following headers must be included in the API call:
| Name | Required | Description |
|---|---|---|
| x-api-key | Yes | The API key generated from the Admin pages in Specops Authentication Web. |
| Content-Type | Yes | Set to application/json. Required on every request that sends a body. |
Note
The reachability check (/api/v2/Ping) is the only endpoint that does not require an API key.
Expected Polling Flow
You start a stream by sending a request with a from timestamp. For each subsequent request, send the cursor returned in the previous response. This same polling loop catches up on existing events and then continues fetching new ones, as long as the same from value and cursor are used.
-
Check reachability (optional)
Parameter Value Method GET Endpoint /api/v2/Ping Parameters - None
Description Confirm the API is reachable before sending authenticated traffic. No API key required. -
Start the stream
Parameter Value Method POST Endpoint /api/v2/SearchLogEvents Body Parameters - from
- pageSize (optional)
Description Open the stream from a chosen start time. Send fromwithout a cursor on this first request only.frommust be within the last 7 days. The response returns the first events plus anextCursor. -
Continue the stream
Parameter Value Method POST Endpoint /api/v2/SearchLogEvents Body Parameters - from (the same value used in step 2)
- cursor
Description Call again with the same fromandcursorset to thenextCursorfrom the previous response. The cursor records your exact position, so each call returns only events you have not seen yet. Repeat this process for each subsequent request. -
Stay live
When a response has
hasMoreResults: falseyou have reached the end of the events currently available. Keep thenextCursor, wait a few seconds, then send another request using the samefromvalue and the samenextCursor. As new events arrive, they resume from the point where the previous response ended. Nothing duplicated, nothing missed. Continue this loop indefinitely.Note
Never change
fromor drop the cursor while streaming. Because the cursor pins your position,fromis allowed to age past 7 days while the stream remains active. The 7-day limit applies only when starting a new stream without a cursor (step 2). Run only a single consumer per API key, because multiple consumers sharing the same key will compete and trip the rate limit.
Example of a complete polling session
The example below walks through a single stream, from the first request to staying live. Cursor values are opaque, so they are shown in abbreviated form.
1. Open the stream: send from with no cursor:
The response returns the first events, hasMoreResults: true, and a nextCursor:
{
"maxResultsPerPage": 100,
"hasMoreResults": true,
"nextCursor": "eyJTb3J0Ijpb...abbreviated...Um9sbGluZyJ9",
"logEvents": [
{
"timestamp": "2026-06-05T08:01:12Z",
"eventId": "UserPasswordReset",
"eventRecordId": "9f1c8a3e-4b2d-4c7a-9b10-2f5e8d6a1c44",
"eventDetails": {
"upn": "jane.doe@contoso.com",
"ipAddress": "203.0.113.42",
"resource": "Reset Password",
"identityServiceId": "Gatekeeper"
}
}
]
}
eventId identifies the type of event; the value shown here is an example. Any eventDetails fields that do not apply to an event are returned as null.
2. Continue: send the same from and the nextCursor from the previous response:
Keep repeating this. Each response gives you the next batch and a fresh nextCursor — while hasMoreResults is true.
3. Stay live: when you are caught up, the response has hasMoreResults: false and an empty logEvents, but it still includes a nextCursor:
{
"maxResultsPerPage": 100,
"hasMoreResults": false,
"nextCursor": "eyJTb3J0Ijpb...abbreviated2...Um9sbGluZyJ9",
"logEvents": []
}
Wait a few seconds, then send the same request again using the same from value and the latest nextCursor. As soon as new events are available, they are returned and hasMoreResults becomes true again. Repeat this loop indefinitely.
If the cursor is rejected or lost
You only need a time-based restart when you have lost your place, for example, if the consumer restarted without a stored cursor.
To restart, open a new stream (step 2, no cursor) with from set to the newest event timestamp you have already processed, minus a few seconds of overlap. Then resume the loop from step 3. The overlap can resend a few events you have already seen, so de-duplicate on eventRecordId to keep only one copy of each event. This is a recovery path only. During normal streaming, stay on the cursor and do not reset from.
API parameter description
| Parameter name | Description |
|---|---|
| from | The start time of the stream. Must be within the last 7 days when starting a new stream (no cursor). When a cursor is supplied, keep sending the same from; it is not re-checked against the 7-day window, so a long-running stream is never forced to reset. |
| pageSize | Optional. Number of events per page (1–1000). Defaults to 100. |
| cursor | The nextCursor value from the previous response. Send it on every follow-up request to continue the stream from your exact position. |
| hasMoreResults | true when more events are available right now, keep calling immediately. false when you are caught up, keep the cursor, wait a few seconds, and poll again. |
| nextCursor | The cursor to send on the next request. Returned on every response, including when hasMoreResults is false, so always carry it forward. |
| timestamp | When an event occurred. Track the newest value you have processed as a restart anchor (see If the cursor is rejected or lost). |
| eventRecordId | A unique identifier for each event. Use it to de-duplicate events after a restart or recovery. |
Note
All timestamps, including the from value in the request and the timestamp on each event, use ISO 8601 format with a time zone, for example 2026-05-28T12:34:56Z (UTC) or 2026-05-28T14:34:56+02:00.
Throttling
The API enforces a per-key rate limit. If you call too often, you will receive an HTTP 429 response.
Note
Treat the HTTP 429 response as a signal to slow down, not as a failure. Pause briefly, retry, and space your calls out a little more. A few seconds between the request cycles is plenty.
Important information
- The API never pushes events to you. There are no webhooks, so nothing arrives on its own. Your client has to keep asking (polling) for new events on a schedule.
- Normal cursor streaming does not return duplicates. As long as you keep sending the cursor, each event is returned exactly once. Duplicates only appear after a restart or recovery, where a deliberate time overlap may resend a few events. Use the unique
eventRecordIdto drop them so each event is stored only once. - Keep your API key secret. Anyone with the key can read your events. Store it securely, and rotate it regularly or if you suspect it has been exposed.