# API keys — PapaProxy.net API reference

Page: https://papaproxy.net/developers/reference/api-keys.php
Last updated: November 9, 2026
Base URL: `https://papaproxy.net/account_new`. Authentication and conventions: https://papaproxy.net/developers/reference/

### List API keys

`GET /api/v1/customers/me/api-keys`

- Access: API key required

List your API keys. Works only in a signed-in Dashboard session; a request made with an API key returns `403`.

Request examples:

cURL:

```bash
curl -X GET "https://papaproxy.net/account_new/api/v1/customers/me/api-keys" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY"
```

Python:

```python
import os

import requests

resp = requests.get(
    "https://papaproxy.net/account_new/api/v1/customers/me/api-keys",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/customers/me/api-keys", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
  },
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/customers/me/api-keys');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/customers/me/api-keys", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `200` — Your API keys (the secret itself is never returned again).

Example response:

```json
[
  {
    "id": "ab1deea7-362b-5dd5-83b7-0a58f0c819a0",
    "name": "ci-deploy",
    "key_prefix": "fb_4f1c9a7e2",
    "scopes": [],
    "is_active": false,
    "created_at": "2026-11-07T14:14:53.656051Z",
    "last_used_at": "2026-11-07T14:14:55.980597Z",
    "last_used_ip": "198.51.100.24",
    "expires_at": null
  },
  {
    "id": "29066e86-06b5-5c5f-9576-519cf1dc2fb8",
    "name": "ci-deploy",
    "key_prefix": "fb_4f1c9a7e2",
    "scopes": [],
    "is_active": false,
    "created_at": "2026-11-07T14:09:29.016284Z",
    "last_used_at": null,
    "last_used_ip": null,
    "expires_at": null
  }
]
```

| Field | Type | Required | Description |
|---|---|---|---|
| `[].id` | string (uuid) | yes | Unique ID. |
| `[].name` | string | yes |  |
| `[].key_prefix` | string | yes | First characters of the key, to tell keys apart. |
| `[].scopes` | array of string | yes | Scopes granted to the key. |
| `[].is_active` | boolean | yes | Whether it's active. |
| `[].created_at` | string (date-time) | yes | When it was created (ISO 8601, UTC). |
| `[].last_used_at` | string (date-time), nullable | no | When the key was last used (ISO 8601, UTC). |
| `[].last_used_ip` | string, nullable | no | IP address the key was last used from. |
| `[].expires_at` | string (date-time), nullable | no | When the current term ends (ISO 8601, UTC). |

- `401` — The API key is missing, malformed, revoked, or expired, or the account is inactive.

  Example:

  ```json
  {"detail": "Invalid or revoked API key."}
  ```

- `403` — The request was made with an API key. Manage keys in the Dashboard.

  Example:

  ```json
  {"detail": "api_key_management_requires_session"}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```


### Create an API key

`POST /api/v1/customers/me/api-keys`

- Access: API key required

Create an API key with the given scopes. The secret is returned only once, in this response, so store it securely. Works only in a signed-in Dashboard session; a request made with an API key returns `403`.

Request body (JSON):

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | Min length: 1 · Max length: 255 |
| `scopes` | array of string | yes | Scopes granted to this key. · Min items: 1 |

Request examples:

cURL:

```bash
curl -X POST "https://papaproxy.net/account_new/api/v1/customers/me/api-keys" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-key", "scopes": ["services:read", "wallet:read"]}'
```

Python:

```python
import os

import requests

resp = requests.post(
    "https://papaproxy.net/account_new/api/v1/customers/me/api-keys",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    json={
        "name": "my-key",
        "scopes": [
            "services:read",
            "wallet:read"
        ]
    },
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/customers/me/api-keys", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "my-key",
    "scopes": [
      "services:read",
      "wallet:read"
    ]
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/customers/me/api-keys');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'my-key',
        'scopes' => ['services:read', 'wallet:read']
    ]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	req, err := http.NewRequest("POST", "https://papaproxy.net/account_new/api/v1/customers/me/api-keys", strings.NewReader(`{"name": "my-key", "scopes": ["services:read", "wallet:read"]}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `201` — Returned once on creation — includes the raw key (shown only here).

Example response:

```json
{
  "id": "3aeee9b3-41a1-5a66-a735-a475b0aebe55",
  "name": "ci-deploy",
  "key_prefix": "fb_4f1c9a7e2",
  "scopes": [
    "services:read"
  ],
  "is_active": true,
  "created_at": "2026-11-07T14:31:35.953143Z",
  "last_used_at": null,
  "last_used_ip": null,
  "expires_at": null,
  "api_key": "fb_4f1c9a7e2b6d80534f1c9a7e2b6d80534f1c9a7e2b6d80534f1c9a7e2b6d8053"
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string (uuid) | yes | Unique ID. |
| `name` | string | yes |  |
| `key_prefix` | string | yes | First characters of the key, to tell keys apart. |
| `scopes` | array of string | yes | Scopes granted to the key. |
| `is_active` | boolean | yes | Whether it's active. |
| `created_at` | string (date-time) | yes | When it was created (ISO 8601, UTC). |
| `last_used_at` | string (date-time), nullable | no | When the key was last used (ISO 8601, UTC). |
| `last_used_ip` | string, nullable | no | IP address the key was last used from. |
| `expires_at` | string (date-time), nullable | no | When the current term ends (ISO 8601, UTC). |
| `api_key` | string | yes | Full secret key — shown ONCE, store it now |

- `401` — The API key is missing, malformed, revoked, or expired, or the account is inactive.

  Example:

  ```json
  {"detail": "Invalid or revoked API key."}
  ```

- `403` — The request was made with an API key. Manage keys in the Dashboard.

  Example:

  ```json
  {"detail": "api_key_management_requires_session"}
  ```

- `422` — Validation error: a field is missing or has the wrong type or value.

  Example:

  ```json
  {"detail": "Validation error"}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```


### Revoke an API key

`DELETE /api/v1/customers/me/api-keys/{key_id}`

- Access: API key required

Revoke one of your API keys. Requests made with it fail immediately afterward. Works only in a signed-in Dashboard session; a request made with an API key returns `403`.

Parameters:

| Field | Type | Required | Description |
|---|---|---|---|
| `key_id (path)` | string (uuid) | yes | ID of the api key. |

Request examples:

cURL:

```bash
curl -X DELETE "https://papaproxy.net/account_new/api/v1/customers/me/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $PAPAPROXY_API_KEY"
```

Python:

```python
import os

import requests

resp = requests.delete(
    "https://papaproxy.net/account_new/api/v1/customers/me/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6",
    headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
    timeout=30,
)
resp.raise_for_status()
print(resp.json())
```

Node.js:

```javascript
const resp = await fetch("https://papaproxy.net/account_new/api/v1/customers/me/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
  },
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());
```

PHP:

```php
<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/customers/me/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("DELETE", "https://papaproxy.net/account_new/api/v1/customers/me/api-keys/3fa85f64-5717-4562-b3fc-2c963f66afa6", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Status, string(data))
}
```

Responses:

- `200` — Generic message response.

Example response:

```json
{
  "message": "API key revoked."
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes |  |

- `401` — The API key is missing, malformed, revoked, or expired, or the account is inactive.

  Example:

  ```json
  {"detail": "Invalid or revoked API key."}
  ```

- `403` — The request was made with an API key. Manage keys in the Dashboard.

  Example:

  ```json
  {"detail": "api_key_management_requires_session"}
  ```

- `404` — API key not found, or it doesn't belong to your account.
- `422` — Validation error: a field is missing or has the wrong type or value.

  Example:

  ```json
  {"detail": "Validation error"}
  ```

- `429` — Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.

  Example:

  ```json
  {"detail": "Too Many Requests"}
  ```

