> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# List Projects

GET https://api-gateway.merge.dev/v1/projects

List the organization's projects, oldest first, each with its routing
policy and budget. Pass `next` as `cursor` to read the next page.

Reference: https://docs.merge.dev/merge-gateway/management-api/projects/list-projects

## Authentication

- `Authorization` header (bearer token, required) — A management key (`mgmt_...`), sent as `Authorization: Bearer <key>`. Reading /v1/routing-policies also accepts the organization's gateway API keys; writing there needs a management key.

## Request

### Query parameters

- `cursor` (string, optional, nullable) — The `next` value from the previous page. Omit it for the first page.
- `limit` (integer, optional, default: 20) — Maximum number of results to return, at most 100. Defaults to 20.

## Response

### 200

The list of projects

- `next` (string, required, nullable) — Cursor for the next page: pass it as `cursor`. Null on the last page.
- `previous` (string, required, nullable) — Always null: paging only goes forward.
- `results` (list of ProjectV1Response, required) — The projects on this page.

## Errors

### 400 Bad Request Error

The cursor is invalid.

- `detail` (ErrorResponseDetail, required) — A message, or an object with a `code` to branch on and a `message`.

### 401 Unauthorized Error

The key is missing or invalid.

- `detail` (ErrorResponseDetail, required) — A message, or an object with a `code` to branch on and a `message`.

### 403 Forbidden Error

The key lacks the scope this route needs, or the change is not allowed.

- `detail` (ErrorResponseDetail, required) — A message, or an object with a `code` to branch on and a `message`.

### 422 Unprocessable Entity Error

Validation Error

- `detail` (list of ValidationError, optional)

### 503 Service Unavailable Error

A service the request needs, such as billing or the gateway itself, is temporarily unavailable. Retry later.

- `detail` (ErrorResponseDetail, required) — A message, or an object with a `code` to branch on and a `message`.

## Types

### ProjectV1Response

A project: a group of API keys with its own routing, budget and controls.

- `budget_config` (ProjectBudgetConfigV1Response, required, nullable) — The project's spending budget; null when it has none.
- `created_at` (datetime, required) — When the project was created.
- `description` (string, required, nullable) — What the project is for.
- `id` (string, required) — The project's id.
- `is_active` (boolean, required) — false while the project's prompt injection, DLP and controls overrides are switched off.
- `name` (string, required) — The project's name.
- `organization_id` (string, required) — The organization the project belongs to.
- `routing_policy` (PolicyV2Response, required, nullable) — The project's own routing policy; null when it uses the organization default.
- `slug` (string, required) — Derived from the name at creation and unique in the organization. It doesn't change when the project is renamed.
- `updated_at` (datetime, required) — When the project last changed.
- `uses_organization_default_routing` (boolean, required) — true when the project routes with the organization's default policy, having no policy of its own.

### ErrorResponseDetail

A message, or an object with a `code` to branch on and a `message`.

- string
- ErrorDetail: ErrorDetail

### ValidationError

- `loc` (list of ValidationErrorLocItems, required)
- `msg` (string, required)
- `type` (string, required)
- `ctx` (ValidationErrorCtx, optional)
- `input` (any, optional)

### ProjectBudgetConfigV1Response

The project's spending budget.

- `amount` (double, required) — Budget amount in USD.
- `enforcement_mode` (enum, required) — `hard_limit` alerts, then refuses requests with `402 project_budget_exceeded` until the period resets or the budget is raised. `soft_limit` only alerts.
  - Allowed values: `soft_limit`, `hard_limit`
- `period` (enum, required) — How often the budget resets.
  - Allowed values: `daily`, `weekly`, `monthly`, `quarterly`, `yearly`
- `alert_thresholds` (list of integer, optional) — Alert percentages, like [80, 90], sorted ascending without duplicates.
- `notification_emails` (list of string, optional) — Addresses emailed when a threshold is reached, in the order sent with duplicates dropped. Recipients don't need a Merge account.
- `notify_org_admins` (boolean, optional, default: true) — Email every organization admin alongside `notification_emails`. Naming recipients adds to the audience rather than replacing it; set false to email only the named recipients. While `notification_emails` is empty, one admin is notified.

### PolicyV2Response

A routing policy: how the gateway picks the model for a request.

- `allowed_models` (list of string, required, nullable) — INTELLIGENT only. In the order they were sent.
- `allowed_providers` (list of string, required, nullable) — INTELLIGENT only. The providers of allowed_models, sorted. Always rebuilt from allowed_models; never stored.
- `axis` (enum, required, nullable) — INTELLIGENT only, else null. What the router optimizes for: `COST`, `PERFORMANCE` or `CAPABILITY`.
  - Allowed values: `COST`, `PERFORMANCE`, `CAPABILITY`
- `created_at` (datetime, required) — When the policy was created.
- `description` (string, required, nullable) — What the policy is for.
- `id` (string, required) — The policy's id. It stays the same across updates.
- `model` (string, required, nullable) — The ready-to-send model string for this policy, like "@alias/fast-experiment". Send it as the `model` in any gateway request to route with this policy.
- `modified_at` (datetime, required) — When the policy last changed.
- `name` (string, required) — The policy's name.
- `priority_order` (list of PriorityModel, required, nullable) — PRIORITY only. Sorted by priority.
- `slug` (string, required, nullable) — The policy's model alias as a bare name, like "fast-experiment". Null only when no alias could be made, such as an organization at its alias limit.
- `strategy` (enum, required) — `PRIORITY` tries `priority_order` in turn; `INTELLIGENT` picks among `allowed_models` per request, optimizing for `axis`.
  - Allowed values: `PRIORITY`, `INTELLIGENT`

### ErrorDetail

A structured error. Some carry extra fields, like `count`.

- `code` (string, required) — A stable code to branch on, like `payment_method_required`.
- `message` (string, required) — A human-readable explanation.

### ValidationErrorLocItems

- string
- integer

### ValidationErrorCtx

### PriorityModel

One model in a PRIORITY policy's fallback order.

- `model` (string, required) — Canonical `provider/model` id.
- `priority` (integer, required) — Lower tries first.

## Examples

**Response**

```json
{
  "next": null,
  "previous": null,
  "results": [
    {
      "budget_config": {
        "amount": 500,
        "enforcement_mode": "hard_limit",
        "period": "monthly",
        "alert_thresholds": [
          50,
          80,
          90
        ],
        "notification_emails": [
          "finance@acme.com"
        ],
        "notify_org_admins": true
      },
      "created_at": "2026-06-02T17:04:00Z",
      "description": "Customer support assistant.",
      "id": "8b6f2d4a-1c3e-4b5a-9f7d-2e0c6a8b4d91",
      "is_active": true,
      "name": "support-bot",
      "organization_id": "3f2b8c1e-6a4d-4e7f-9b21-5c8d0e7a4f13",
      "routing_policy": {
        "allowed_models": null,
        "allowed_providers": null,
        "axis": null,
        "created_at": "2026-06-02T17:04:00Z",
        "description": "Claude first, GPT-5.5 as the fallback.",
        "id": "c4e1a7b2-9d3f-4a6e-8b5c-1f2d3e4a5b6c",
        "model": null,
        "modified_at": "2026-06-02T17:04:00Z",
        "name": "support-bot routing policy",
        "priority_order": [
          {
            "model": "anthropic/claude-sonnet-4-6",
            "priority": 1
          },
          {
            "model": "openai/gpt-5.5",
            "priority": 2
          }
        ],
        "slug": null,
        "strategy": "PRIORITY"
      },
      "slug": "support-bot",
      "updated_at": "2026-09-14T09:30:00Z",
      "uses_organization_default_routing": false
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api-gateway.merge.dev/v1/projects"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api-gateway.merge.dev/v1/projects';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api-gateway.merge.dev/v1/projects"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api-gateway.merge.dev/v1/projects")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api-gateway.merge.dev/v1/projects")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api-gateway.merge.dev/v1/projects', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api-gateway.merge.dev/v1/projects");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api-gateway.merge.dev/v1/projects")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```