AI Power Ups
Contents

Start

Quickstart

Key, first typed call, next page, one record. The example uses academic.search, a free source, so it costs 0 credits and works on the Free plan.

1. Get a key

Sign up or log in, then create a key under API keys in the dashboard. It looks like apa_live_… and can be copied again from the dashboard at any time. Send it as Authorization: Bearer on every call. Keys are for servers and command lines; browsers on other origins cannot call the API (see keys and security).

2. Call it with curl

sh
export AIPA_API_KEY=apa_live_…   # dashboard → API keys → Create a key; a server-side secret
API=https://api.powerups-ai.store

# 1. Confirm the key and balance
curl -s $API/v1/account -H "Authorization: Bearer $AIPA_API_KEY"

# 2. Execute a typed search (free source, 0 credits)
curl -s -X POST $API/v1/capabilities/academic.search/execute \
  -H "Authorization: Bearer $AIPA_API_KEY" -H "Content-Type: application/json" \
  -d '{"query":"transformer attention","sort":"cited","num":3}'

# 3. Next page (same search_id from step 2)
curl -s -X POST $API/v1/follow-up -H "Authorization: Bearer $AIPA_API_KEY" \
  -H "Content-Type: application/json" -d '{"search_id":"srch_…","action":"more"}'

# 4. Open a record (free for this source; a repeat is served from cache)
curl -s -X POST $API/v1/follow-up -H "Authorization: Bearer $AIPA_API_KEY" \
  -H "Content-Type: application/json" -d '{"record_id":"rec_…","action":"details"}'

The execute response is the execute envelope:

execute envelope (illustrative values)
{
  "search_id": "srch_ePaazJiKYqmgc0hK",
  "capability": "academic.search",
  "results": [
    {
      "record_id": "rec_siQNelpEPOWay4PB",
      "title": "Attention Is All You Need",
      "url": "https://doi.org/10.48550/arxiv.1706.03762",
      "snippet": "…",
      "year": 2017,
      "authors": [
        "Ashish Vaswani",
        "…"
      ],
      "cited_by": 12345,
      "openalex_id": "W2963403868"
    }
  ],
  "result_count": 3,
  "has_more": true,
  "next": {
    "url": "/v1/follow-up",
    "actions": [
      "more",
      "update"
    ],
    "example": {
      "search_id": "srch_ePaazJiKYqmgc0hK",
      "action": "more"
    }
  },
  "record_actions": [
    "details"
  ],
  "charged_credits": 0,
  "balance": {
    "used": 12,
    "cap": 500,
    "period_end": "2026-10-19T17:35:40.303Z"
  }
}
  • search_id and every record_id are minted by the API and live for 24 hours after last use.
  • next.actions lists the search actions this capability accepts and next.example is a follow-up body that is valid right now (or null when nothing applies).
  • charged_credits and balance are on every metered response.

3. The same in TypeScript, typed

Generate types from the public OpenAPI document once at build time; the document has one operation per capability, so the body of each path is typed:

sh
# In a new Node.js 20+ project, install this checked generator/compiler pair
npm install --save-dev --save-exact openapi-typescript@7.13.0 typescript@5.9.3 @types/node@20.19.0
npm install --save-exact openapi-fetch@0.17.0

# Generate types; preserve optional request fields with server-side defaults
npx openapi-typescript https://api.powerups-ai.store/v1/openapi.public.json -o ./ai-power-ups.d.ts --default-non-nullable=false
usage.mts
import createClient from "openapi-fetch";
import type { paths } from "./ai-power-ups.js";

const client = createClient<paths>({
  baseUrl: "https://api.powerups-ai.store",
  headers: { Authorization: `Bearer ${process.env.AIPA_API_KEY}` }, // server-side only
});

const { data, error, response } = await client.POST("/v1/capabilities/academic.search/execute", {
  body: { query: "transformer attention", sort: "cited", num: 3 },
});
if (error) throw new Error(`${response.status} ${error.error.code}: ${error.error.message}`);

for (const record of data.results) console.log(record.title, record.url);

if (data.has_more) {
  const more = await client.POST("/v1/follow-up", {
    body: { search_id: data.search_id, action: "more" },
  });
  // more.data is the same envelope with the same search_id
}
Check the example
npx tsc usage.mts --module NodeNext --target ES2022 --strict --noEmit

4. The same in Python

Python 3 (standard library only)
import json, os, urllib.request

API = "https://api.powerups-ai.store"
KEY = os.environ["AIPA_API_KEY"]  # server-side secret

def call(method, path, body=None):
    data = None if body is None else json.dumps(body).encode()
    req = urllib.request.Request(API + path, data=data, method=method, headers={
        "Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=60) as res:
            return res.status, json.load(res)
    except urllib.error.HTTPError as err:
        return err.code, json.load(err)

status, page = call("POST", "/v1/capabilities/academic.search/execute",
                    {"query": "transformer attention", "sort": "cited", "num": 3})
if status != 200:
    raise SystemExit(f"{status} {page['error']['code']}: {page['error']['message']}")
for record in page["results"]:
    print(record["title"], record.get("url"))
if page["has_more"]:
    status, page = call("POST", "/v1/follow-up", {"search_id": page["search_id"], "action": "more"})

What to expect

  • GET /v1/account returns 200 with credits.cap of at least 500 on the Free plan.
  • Execute returns 200, capability academic.search, result_count 3, charged_credits 0.
  • more returns 200 with the same search_id and new record ids.
  • A second details on the same record returns cached: true and charged_credits 0.
  • An unknown key in the body returns 400 with violations[0].path naming it.
  • A bad key returns 401 with an x-ratelimit-limit: 60 header.
Run calls for one account sequentially: the API admits one metered call per account at a time and answers a concurrent one with 429 rate_limited. Details in credits and limits.