Authentication
View as MarkdownHow to authenticate with the PostGrad Knowledge API using API keys.
API Keys
PostGrad uses API keys to authenticate requests to the Knowledge API. Keys use the pg_live_ prefix for production environments and pg_test_ for test environments.
To get an API key:
- Sign up at postgrad.io and subscribe to a plan
- Go to Dashboard > API Keys
- Click Create New Key and give it a descriptive name
- Copy the key immediately -- it is shown only once
Store your API key securely. If you lose it, you can revoke the old key and create a new one from the dashboard.
Using Your API Key
Every knowledge request needs two headers:
Authorization: Bearer pg_live_xxxxxxxxxxxxx ← your API key
X-PostGrad-Feed: <feed-uuid> ← which feed to queryThe X-PostGrad-Feed header tells PostGrad which of your subscribed feeds the request should run against. Call GET /api/v1/feeds (no feed header required) to list feed ids for your account.
Accepted values:
- A feed UUID — scopes the request to that one feed.
all— search every feed you're actively subscribed to and get merged, relevance-ranked results. Each hit includesfeed_id+feed_name. Fan-out counts as one request against your monthly quota. Currently supported on/knowledge/searchonly.- Omitted — on search-style endpoints, PostGrad auto-picks your most-populated feed and returns which one it used in the
X-PostGrad-Feed-Auto-Selectedresponse header. On list/fetch-by-id endpoints, the header is required.
Legacy compatibility: the feed id is also accepted as a ?feed_id=<uuid> query parameter or { "feed_id": "<uuid>" } JSON body field. Both paths return a Warning: 299 postgrad response header asking you to migrate to the X-PostGrad-Feed header form. Prefer the header for all new integrations.
curl
curl -H "Authorization: Bearer pg_live_xxxxxxxxxxxxx" \
-H "X-PostGrad-Feed: b1a2c3d4-e5f6-7890-abcd-ef0123456789" \
https://postgrad.io/api/v1/knowledgeJavaScript (fetch)
const response = await fetch('https://postgrad.io/api/v1/knowledge', {
headers: {
'Authorization': 'Bearer pg_live_xxxxxxxxxxxxx',
'X-PostGrad-Feed': 'b1a2c3d4-e5f6-7890-abcd-ef0123456789',
},
});
const data = await response.json();JavaScript (axios)
import axios from 'axios';
const client = axios.create({
baseURL: 'https://postgrad.io/api/v1',
headers: {
'Authorization': 'Bearer pg_live_xxxxxxxxxxxxx',
'X-PostGrad-Feed': 'b1a2c3d4-e5f6-7890-abcd-ef0123456789',
},
});
const { data } = await client.get('/knowledge');Python (requests)
import requests
response = requests.get(
"https://postgrad.io/api/v1/knowledge",
headers={
"Authorization": "Bearer pg_live_xxxxxxxxxxxxx",
"X-PostGrad-Feed": "b1a2c3d4-e5f6-7890-abcd-ef0123456789",
},
)
data = response.json()Tier Access
Your subscription tier determines rate limits, monthly quotas, and which search modes are available. All tiers have access to every feed and category they're subscribed to — there are no category restrictions by tier.
| Feature | Starter | Pro | Scale |
|---|---|---|---|
| Rate limit | 10 req/min | 30 req/min | 100 req/min |
| Monthly quota | 1,000 requests | 10,000 requests | 50,000 requests |
| Search modes | Keyword | Keyword + Semantic | Keyword + Semantic + Hybrid |
| Feed access | All subscribed | All subscribed | All subscribed |
Search Modes (available on every tier)
As of 2026-06-20, all three search modes work on every tier. Mode is a per-query tuning choice, not a paid upgrade. Tiers differ on volume (monthly queries), rate limit (per-minute burst), and ops features (webhooks, priority/dedicated support) — not on retrieval capability.
| Mode | Availability | Description |
|---|---|---|
keyword | All tiers | Postgres full-text search (tsvector + ts_rank). Best for exact-term / acronym / SKU queries. |
semantic | All tiers | Gemini 768-dim embeddings + pgvector cosine distance. Best for paraphrase / conceptual queries. Query embedding is Upstash-cached for 24h. |
hybrid | All tiers | Reciprocal Rank Fusion of keyword + semantic (k=60). Best for general-purpose agents with heterogeneous query distributions. |
For a full explanation of each mode and how to pick, see MCP Integration → Search modes explained.
No tier returns TIER_INSUFFICIENT for a search mode anymore — request
keyword, semantic, or hybrid freely on any plan.
Error Responses
All errors follow the standard response envelope with data: null and an error object.
401 Unauthorized
Returned when the API key is missing or invalid.
{
"data": null,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}Complete Error Reference
Every error returns the envelope { "data": null, "error": { "code", "message", "details?" } }. Some include a details object with actionable fields (retry time, upgrade URL, feed ID).
| HTTP | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Request body / query params missing or malformed | Fix the request and retry |
| 400 | INVALID_FEED | X-PostGrad-Feed header missing or malformed UUID on a multi-feed account | Call GET /api/v1/feeds to discover valid feed IDs |
| 401 | UNAUTHORIZED | Missing, invalid, or revoked API key | Check your Authorization: Bearer header. If the key is revoked, create a new one in the dashboard |
| 403 | SUBSCRIPTION_INACTIVE | Your PostGrad platform subscription is past_due or canceled | Reactivate at /dashboard/settings?tab=billing |
| 403 | FEED_NOT_SUBSCRIBED | You're not subscribed to the feed you tried to query | Subscribe via the dashboard or the subscribe_feed MCP tool |
| 403 | TIER_INSUFFICIENT | Reserved for tier-gated features. Search modes are no longer gated — all modes work on all tiers — so you will not hit this for mode=. | Upgrade your tier only if a future tier-gated feature returns this |
| 404 | NOT_FOUND | Requested entry/resource doesn't exist | Double-check the ID |
| 404 | FEED_UNAVAILABLE | Feed exists but has been archived or suspended | Try a different feed — check /api/v1/feeds |
| 412 | LEGAL_REVIEW_REQUIRED | You must accept updated terms before continuing | Visit the accept_url in details (usually /signup/legal) |
| 412 | STRIPE_CONNECT_REQUIRED | Licensor action needs Stripe Connect setup (e.g. creating a paid feed) | Visit /dashboard/connect to finish Connect onboarding |
| 429 | RATE_LIMIT_EXCEEDED | Per-minute or monthly quota exceeded | Respect the Retry-After response header |
| 500 | INTERNAL_ERROR | Unexpected server error | Retry with exponential backoff. If it persists, check status page |
| 503 | SERVICE_UNAVAILABLE | Upstream dependency (Gemini, Supabase) is degraded | Retry with backoff |
Handling 429 Rate Limits
Rate-limit responses include a Retry-After header specifying how many seconds to wait:
HTTP/2 429
Retry-After: 45Handling rate limits:
- Always respect
Retry-After— exponential backoff on top is a bonus but the header value is the floor - Check the
meta.queries_remainingfield in successful responses to monitor usage - Use the GET /usage endpoint to check your current period consumption
- Consider upgrading your tier if you consistently hit limits
MCP Errors
MCP tools return errors wrapped in the tool result's content[0].text as a JSON string, with isError: true on the response. The error code values match REST API codes above, so you can write shared retry logic:
{
"content": [{ "type": "text", "text": "{\"error\":\"FEED_NOT_SUBSCRIBED\",\"message\":\"...\"}" }],
"isError": true
}One MCP-only code exists: CATEGORY_RESTRICTED (403-equivalent) — your API key is limited to a subset of categories via an admin-set allowed_categories override.
Key Rotation
To rotate an API key with zero downtime:
- Create a new key from Dashboard > API Keys
- Update your integration to use the new key
- Verify the new key works by making a test request
- Revoke the old key from the dashboard
By creating the new key before revoking the old one, your integration experiences no interruption. Both keys remain active during the transition window.
Security Best Practices
- Never commit keys to version control. Use
.envfiles or a secrets manager. - Use environment variables to store keys in your application:
export POSTGRAD_API_KEY=pg_live_xxxxxxxxxxxxx - Use different keys for different environments. Create separate keys for development, staging, and production.
- Use different keys for different services. If multiple services consume the API, give each its own key for independent revocation.
- Revoke unused keys promptly. Remove keys for decommissioned services or departed team members.
- Monitor usage regularly. Check the dashboard or the
/usageendpoint for unexpected spikes that could indicate a leaked key.
Browser usage and CORS
The REST API is server-side only. PostGrad intentionally does not emit CORS headers on its /api/v1/* endpoints — a browser request will fail at preflight with no Access-Control-Allow-Origin. This is by design: a pg_live_* API key embedded in browser code can be lifted from the page source or DevTools network tab, and a leaked key can burn through your monthly quota or be used to query feeds you've paid for.
You have three options for browser-facing applications:
- Proxy through your server. Your frontend calls your backend; your backend holds the API key and calls PostGrad. Standard pattern, no key exposure.
- Use the MCP custom connector. https://postgrad.io/docs/mcp-integration — agents authenticate via OAuth in the browser, no API key in client code.
- Run a Cloudflare/Vercel edge worker that holds the key in secrets and proxies requests, with your own rate-limit + origin check in front.
If you have a legitimate use case for direct browser-side access (e.g. an internal dashboard on a private network), contact support and we can issue a key with a constrained origin allow-list.