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

# List Catalog Providers

GET https://api.onepin.ai/api/v1/providers

List all available speech synthesis providers in the catalog.

Returns the speech synthesis providers available to your account — each with its
display name, number of available models, a `beta` badge flag, and a HATEOAS
`models` link to `GET /providers/{provider}/models`. The response contains only
customer-facing metadata; cost, credentials, and base URLs are never included.

Staff can restrict a provider or an individual model to internal accounts or to
paying customers. Restricted entries are omitted from this list entirely rather
than returned and marked, so the list is exactly what your account may use. `beta`
is a display badge only and never affects availability; a provider carries it only
when EVERY model available to you under it is beta, so a single experimental model
among mature ones is badged on the model rather than on the provider.

This endpoint is the starting point for building a provider/model/voice
selection flow. The typical traversal is: list providers → follow `models`
link → follow `voices` link for the chosen model. Requires `X-Workspace-Id`
and the `catalog:read` scope.

Reference: https://onepin.ai/docs/reference/api-reference/providers/list-catalog-providers-api-v-1-providers-get

## Authentication

- `Authorization` header (bearer token, required) — Clerk JWT token
- `Authorization` header (bearer token, required) — Onepin live API key (`op_live_...`). Test and public keys are reserved in Phase 1.

## Servers

- `https://api.onepin.ai` (prod, default)
- `https://dev-api.onepin.ai` (dev)

## Request

### Headers

- `X-Workspace-Id` (string, optional, nullable)

## Response

### 200

Successful Response

- `data` (list of CatalogProviderOut, required)
- `meta` (Meta, required)
- `pagination` (PaginationMeta, required)

## Errors

### 422 Unprocessable Entity Error

Validation Error

- `detail` (list of ValidationError, optional)

## Types

### CatalogProviderOut

Catalog provider entry — lean, customer-safe. Sourced from the in-memory ``ProviderRegistry`` (TTS / ``operator`` kind). Cost, credentials, base_url, and enabled flags are excluded by construction.

- `provider` (string, required)
- `display_name` (string, required)
- `kind` (string, required)
- `model_count` (integer, required)
- `beta` (boolean, optional, default: false)
- `links` (map from string to CatalogLink, optional)

### Meta

- `request_id` (string, required)
- `timestamp` (datetime, required)

### PaginationMeta

- `limit` (integer, required)
- `next` (string, optional, nullable)
- `prev` (string, optional, nullable)

### ValidationError

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

### CatalogLink

HATEOAS link to a nested catalog resource (HAL-FORMS-flavored). The server emits a concrete, ready-to-call ``href`` (path segments already filled in and URL-encoded) so the FE never constructs catalog URLs itself.

- `href` (string, required)
- `method` (string, optional, default: GET)

### ValidationErrorLocItems

### ValidationErrorCtx

## Examples

**Response**

```json
{
  "data": [
    {
      "provider": "string",
      "display_name": "string",
      "kind": "string",
      "model_count": 1,
      "beta": false,
      "links": {}
    }
  ],
  "meta": {
    "request_id": "string",
    "timestamp": "2024-01-15T09:30:00Z"
  },
  "pagination": {
    "limit": 1,
    "next": "string",
    "prev": "string"
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.onepin.ai/api/v1/providers"

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

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

print(response.json())
```

```javascript
const url = 'https://api.onepin.ai/api/v1/providers';
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.onepin.ai/api/v1/providers"

	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.onepin.ai/api/v1/providers")

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.onepin.ai/api/v1/providers")
  .header("Authorization", "Bearer <token>")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.onepin.ai/api/v1/providers', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.onepin.ai/api/v1/providers");
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.onepin.ai/api/v1/providers")! 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()
```