Skip to main content

Using the Public API v1 Audit Log APIs

This guide explains how to retrieve audit logs via the Public API using the search and query endpoints. The main idea: you use a timestamp once to find a starting point, then use log IDs for all actual retrieval and pagination.

Why log IDs instead of timestamps?

  • Search by time is supported only to get a starting point (the first log at or after a given time). The API returns one log and its ID.
  • Fetching and pagination use that log ID (and then the ID of the last log in each page). We do not support “give me all logs between 10:00 and 10:15” by timestamp in a single call.
Reasons we key off IDs:
  1. Stable ordering — Logs are ordered by ID. IDs are unique and never change, so pagination is consistent even if many events share the same second.
  2. No gaps or duplicates — Using “last seen ID” as the cursor avoids skipping or duplicating logs when events are dense.
  3. Clear “next page” — The next request is always “logs after this ID,” which is unambiguous.
So: think of the timestamp as “where do I start?” and the log ID as “where am I in the stream?” for all subsequent requests.

The two endpoints

PurposeEndpointYou provideYou get
Find a starting log by timeGET /api/v1/logs/audit/search?time=<timestamp>A timestamp (e.g. 10:00 AM)One log object — the first log at or after that time. That log’s id is your cursor.
Fetch logs after a given logGET /api/v1/logs/audit?from=<log_id>&take=<n>A log ID (from) and how many to fetch (take, 1–1000)Up to take logs whose ID is after from, in ascending order by ID.
Important details:
  • Search returns a single log: the first one with timestamp >= your time. The response body is { "log": { "id": "...", "timestamp": "...", ... } }. You need that log.id for the next step.
  • Query returns logs with ID strictly greater than from. So the log you got from search is not included in the first query response — it is the first log in your time range; you add it to your results yourself, then use its ID to fetch the rest.
Base URL for the Public API: https://api.harvey.ai (or your environment’s base URL). Full paths are /api/v1/logs/audit/search and /api/v1/logs/audit. See developers.harvey.ai for the full API reference.

Example 1: “All logs since 10:00 AM”

Goal: at 10:15 AM, get every audit log from 10:00 AM onward.

Step 1 — Get the first log and its ID (timestamp → ID)

  • Call: GET /api/v1/logs/audit/search?time=<10:00 AM in ISO 8601 or your API’s accepted format>
  • Response: { "log": { "id": "abc-123-...", "timestamp": "2025-03-11T10:00:00.000Z", ... } }
  • Keep this log — it is the first log in your range. Add it to your result set.
  • Save log.id (e.g. abc-123-...) — this is your cursor for the next request.
If the response is an error (e.g. no logs available for that time), there are no logs at or after 10:00 AM; you’re done.

Step 2 — Fetch the next logs (ID-based)

  • Call: GET /api/v1/logs/audit?from=abc-123-...&take=1000
  • Response: { "logs": [ ... ] } — up to 1000 logs whose ID is after abc-123-....
Append these to your result set.

Step 3 — Paginate if needed

  • Each response contains at most take logs (here, 1000). You never get “more than 1000” in one response.
  • If you got exactly 1000 logs, there may be more. Take the last log in the array and use its id as the new from:
    • GET /api/v1/logs/audit?from=<last_log_id>&take=1000
  • Repeat until a response has fewer than 1000 logs — then there are no more.
Your full “logs since 10:00 AM” set is: [log from Step 1] + [all logs from Step 2 and Step 3].

Example 2: “A 15-minute slice starting at 8:00 AM”

Goal: all logs from 8:00 AM to 8:15 AM. You still use search to turn “8:00 AM” into a starting log ID, then query by ID. The only extra part is stopping when logs go past 8:15 AM.

Step 1 — Get the first log at or after 8:00 AM

  • Call: GET /api/v1/logs/audit/search?time=<8:00 AM>
  • Keep the returned log as the first log in your slice.
  • Save log.id for the next request.

Step 2 — Fetch in pages and stop at the end time

  • Call: GET /api/v1/logs/audit?from=<saved_id>&take=1000
  • For each log in logs, check its timestamp:
    • If timestamp <= 8:15 AM → include it in your 15-minute slice.
    • If timestamp > 8:15 AM → you’ve passed the end of the window; stop requesting more and (optionally) drop any later logs from this batch.
  • If you got 1000 logs and the last one is still before or at 8:15 AM, there may be more in that window. Use the last log’s id as from and call again. Repeat until you either get fewer than 1000 logs or you see a timestamp after 8:15 AM.
So: timestamps are used only to (1) find the start via search and (2) filter or stop when you’ve reached your end time. IDs are used for every “give me the next page” request.

Summary

  • Timestamps answer: “Where do I start?” (search) and “Where do I stop?” (your own end-time filter).
  • Log IDs answer: “What’s the next page?” (query’s from and pagination).
  • Always include the log returned by search in your results; the first query call returns only logs after that log’s ID.
  • Pagination: use the last log’s ID from each response as from for the next request; when you get fewer than take logs, you’ve reached the end.
For exact parameter names, formats, and error codes, use the Public API reference for the audit log endpoints.