Skip to content

Instantly share code, notes, and snippets.

@drichardson
Created March 26, 2026 03:58
Show Gist options
  • Select an option

  • Save drichardson/6a52f27593cd198fc293f398c56b12e5 to your computer and use it in GitHub Desktop.

Select an option

Save drichardson/6a52f27593cd198fc293f398c56b12e5 to your computer and use it in GitHub Desktop.
Datadog GET /api/v2/metrics links.next cursor bug reproduction
#!/usr/bin/env -S uv run --with requests
# /// script
# requires-python = ">=3.12"
# dependencies = ["requests"]
# ///
"""
Demonstrates a bug in the Datadog GET /api/v2/metrics API where the cursor
URL returned in links.next is not followable (returns 400 Bad Request).
Requires DD_API_KEY and DD_APP_KEY environment variables.
Usage:
doppler run -- ./scripts/datadog_metrics_cursor_bug.py
"""
import os
import sys
import requests
def main() -> None:
api_key = os.environ.get("DD_API_KEY")
app_key = os.environ.get("DD_APP_KEY")
if not api_key or not app_key:
print("ERROR: DD_API_KEY and DD_APP_KEY must be set", file=sys.stderr)
sys.exit(1)
headers = {
"DD-API-KEY": api_key,
"DD-APPLICATION-KEY": app_key,
}
base_url = "https://api.datadoghq.com"
with requests.Session() as session:
session.headers.update(headers)
# Step 1: fetch first page
url1 = f"{base_url}/api/v2/metrics?filter[configured]=false&page[size]=1000"
print(f"Step 1: GET {url1}")
r1 = session.get(url1)
print(f" Status: {r1.status_code}")
assert r1.status_code == 200, f"Expected 200, got {r1.status_code}"
body1 = r1.json()
page1_count = len(body1["data"])
next_url = body1["links"]["next"]
cursor = body1["meta"]["pagination"]["next_cursor"]
print(f" Items returned: {page1_count}")
print(f" next_cursor length: {len(cursor)} chars")
print(f" links.next length: {len(next_url)} chars")
print()
# Step 2: follow links.next exactly as returned by the API
print(f"Step 2: GET {next_url}")
r2 = session.get(next_url)
print(f" Status: {r2.status_code}")
if r2.status_code == 400:
print()
print("BUG REPRODUCED:")
print(" GET /api/v2/metrics returned a links.next URL that the server")
print(" refuses to follow (400 Bad Request).")
print(
f" The page[cursor] query parameter is {len(cursor)} characters long,"
)
print(" which appears to exceed the server's URL length limit.")
sys.exit(1)
elif r2.status_code == 200:
print(" OK - pagination worked (bug not reproduced)")
else:
print(f" Unexpected status: {r2.status_code}")
print(f" Body: {r2.text[:500]}")
sys.exit(1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment