PostGrad

Authentication

View as Markdown

How 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:

  1. Sign up at postgrad.io and subscribe to a plan
  2. Go to Dashboard > API Keys
  3. Click Create New Key and give it a descriptive name
  4. 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 query

The 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 includes feed_id + feed_name. Fan-out counts as one request against your monthly quota. Currently supported on /knowledge/search only.
  • Omitted — on search-style endpoints, PostGrad auto-picks your most-populated feed and returns which one it used in the X-PostGrad-Feed-Auto-Selected response 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/knowledge

JavaScript (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.

FeatureStarterProScale
Rate limit10 req/min30 req/min100 req/min
Monthly quota1,000 requests10,000 requests50,000 requests
Search modesKeywordKeyword + SemanticKeyword + Semantic + Hybrid
Feed accessAll subscribedAll subscribedAll 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.

ModeAvailabilityDescription
keywordAll tiersPostgres full-text search (tsvector + ts_rank). Best for exact-term / acronym / SKU queries.
semanticAll tiersGemini 768-dim embeddings + pgvector cosine distance. Best for paraphrase / conceptual queries. Query embedding is Upstash-cached for 24h.
hybridAll tiersReciprocal 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).

HTTPCodeWhenWhat to do
400VALIDATION_ERRORRequest body / query params missing or malformedFix the request and retry
400INVALID_FEEDX-PostGrad-Feed header missing or malformed UUID on a multi-feed accountCall GET /api/v1/feeds to discover valid feed IDs
401UNAUTHORIZEDMissing, invalid, or revoked API keyCheck your Authorization: Bearer header. If the key is revoked, create a new one in the dashboard
403SUBSCRIPTION_INACTIVEYour PostGrad platform subscription is past_due or canceledReactivate at /dashboard/settings?tab=billing
403FEED_NOT_SUBSCRIBEDYou're not subscribed to the feed you tried to querySubscribe via the dashboard or the subscribe_feed MCP tool
403TIER_INSUFFICIENTReserved 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
404NOT_FOUNDRequested entry/resource doesn't existDouble-check the ID
404FEED_UNAVAILABLEFeed exists but has been archived or suspendedTry a different feed — check /api/v1/feeds
412LEGAL_REVIEW_REQUIREDYou must accept updated terms before continuingVisit the accept_url in details (usually /signup/legal)
412STRIPE_CONNECT_REQUIREDLicensor action needs Stripe Connect setup (e.g. creating a paid feed)Visit /dashboard/connect to finish Connect onboarding
429RATE_LIMIT_EXCEEDEDPer-minute or monthly quota exceededRespect the Retry-After response header
500INTERNAL_ERRORUnexpected server errorRetry with exponential backoff. If it persists, check status page
503SERVICE_UNAVAILABLEUpstream dependency (Gemini, Supabase) is degradedRetry 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: 45

Handling rate limits:

  • Always respect Retry-After — exponential backoff on top is a bonus but the header value is the floor
  • Check the meta.queries_remaining field 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:

  1. Create a new key from Dashboard > API Keys
  2. Update your integration to use the new key
  3. Verify the new key works by making a test request
  4. 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 .env files 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 /usage endpoint 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:

  1. Proxy through your server. Your frontend calls your backend; your backend holds the API key and calls PostGrad. Standard pattern, no key exposure.
  2. Use the MCP custom connector. https://postgrad.io/docs/mcp-integration — agents authenticate via OAuth in the browser, no API key in client code.
  3. 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.

On this page