Complete guide for the PostHog plugin — REST API access for querying analytics with HogQL, managing feature flags, inspecting events and persons, reading insights, experiments, cohorts, surveys, and more.
This plugin provides access to the PostHog REST API on the user's behalf, using a stored Personal API Key (phx_...).
Capabilities:
Request the stored PostHog credential. The personalApiKey field is an opaque placeholder — the sandbox fetch proxy substitutes the real value automatically. Never decode or transform it.
const cred = await API.getCredential('posthog-pat');
if (!cred) {
return 'PostHog credential is not configured. Ask the user to create a Personal API Key at Settings → Personal API Keys in their PostHog dashboard, then store it in Settings.';
}
PostHog Cloud has two regions. The agent must determine which one the user is on:
| Region | Base URL |
|---|---|
| US Cloud | https://us.posthog.com |
| EU Cloud | https://eu.posthog.com |
How to determine the region:
Store the base URL in a variable and use it for all requests.
All API paths are relative to the region base URL. Always pass the key as a Bearer header:
const BASE = 'https://us.posthog.com'; // or https://eu.posthog.com
async function phGet(path) {
const res = await fetch(`${BASE}${path}`, {
headers: {
Authorization: `Bearer ${cred.personalApiKey}`,
'Content-Type': 'application/json',
},
});
if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
return res.json();
}
async function phPost(path, body) {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${cred.personalApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
return res.json();
}
async function phPatch(path, body) {
const res = await fetch(`${BASE}${path}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${cred.personalApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
return res.json();
}
async function phDelete(path) {
const res = await fetch(`${BASE}${path}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${cred.personalApiKey}`,
'Content-Type': 'application/json',
},
});
if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
if (res.status === 204) return null;
return res.json();
}
Most endpoints require a project ID — a numeric identifier visible in the PostHog dashboard URL: