Skip to content
CatalogsLast updated: October 15, 2026

Iceberg REST Catalog API Reference

The Iceberg REST Catalog API is the OpenAPI-defined HTTP interface engines use for catalog calls: config, namespaces, tables, commits, views, and credentials.

iceberg rest catalog apiiceberg rest api speciceberg rest catalog endpointsiceberg catalog api referenceiceberg rest spec

Iceberg REST Catalog API Reference

This page is a short endpoint cheat sheet. For the full guide covering auth flows, credential vending, server-side scan planning, Apache Polaris setup, and what changed in Iceberg 1.11 and 1.12, see Iceberg REST Catalog Spec: Endpoints, Auth, and Polaris.

The Iceberg REST Catalog specification (introduced in Iceberg 0.14.0 and evolved since) defines a standard HTTP API that any catalog implementation must expose for Iceberg-compatible engines to connect. This is what makes Iceberg truly multi-engine: every engine uses the same REST API to talk to any catalog: Apache Polaris, Project Nessie, the AWS Glue Iceberg REST endpoint, and others.

The spec is formally documented at iceberg.apache.org/spec and the OpenAPI spec at github.com/apache/iceberg.

Base URL and Authentication

Every resource path in the OpenAPI spec sits under /v1/{prefix}/, where the optional prefix is returned by GET /v1/config. The examples below omit the prefix for readability.

All REST Catalog endpoints are relative to a configurable base URI:

Base URI: https://my-catalog.example.com
Prefix: /iceberg  (configurable)
Full base: https://my-catalog.example.com/iceberg

Authentication is provider-specific but typically:

Configuration Discovery: GET /v1/config

The first API call any client makes: fetches catalog-level configuration:

GET /v1/config?warehouse=my-warehouse

Response:

{
  "defaults": {
    "clients": "4"
  },
  "overrides": {
    "warehouse": "s3://my-bucket/warehouse/",
    "credential": "..."
  }
}

Configuration merging: server defaults are applied first, then the client’s own properties, then server overrides, which win.

OAuth Token: POST /v1/oauth/tokens

This endpoint is marked deprecated for removal in the OpenAPI spec. New deployments should point clients at an external identity provider with the oauth2-server-uri property. Older clients still use it to fetch an OAuth2 bearer token:

POST /v1/oauth/tokens
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=my-client-id&
client_secret=my-client-secret&
scope=PRINCIPAL_ROLE:my-role

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiJ9...",
  "token_type": "bearer",
  "expires_in": 3600,
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}

Namespace Operations

List Namespaces: GET /v1/namespaces

GET /v1/namespaces?parent=analytics
Authorization: Bearer <token>

Response:

{
  "namespaces": [
    ["analytics", "bronze"],
    ["analytics", "silver"],
    ["analytics", "gold"]
  ]
}

Create Namespace: POST /v1/namespaces

POST /v1/namespaces
Content-Type: application/json

{
  "namespace": ["analytics", "gold"],
  "properties": {
    "owner": "data-team@company.com",
    "location": "s3://my-bucket/warehouse/analytics/gold/"
  }
}

Get Namespace Properties: GET /v1/namespaces/{namespace}

GET /v1/namespaces/analytics%1Fgold

Response:

{
  "namespace": ["analytics", "gold"],
  "properties": {
    "owner": "data-team@company.com",
    "location": "s3://my-bucket/warehouse/analytics/gold/"
  }
}

Table Operations

List Tables: GET /v1/namespaces/{namespace}/tables

GET /v1/namespaces/analytics%1Fgold/tables

Response:

{
  "identifiers": [
    { "namespace": ["analytics", "gold"], "name": "orders" },
    { "namespace": ["analytics", "gold"], "name": "customers" }
  ]
}

Create Table: POST /v1/namespaces/{namespace}/tables

POST /v1/namespaces/analytics%1Fgold/tables
Content-Type: application/json

{
  "name": "orders",
  "location": "s3://my-bucket/warehouse/analytics/gold/orders/",
  "schema": {
    "type": "struct",
    "fields": [
      {"id": 1, "name": "order_id", "type": "long", "required": true},
      {"id": 2, "name": "customer_id", "type": "long", "required": true},
      {"id": 3, "name": "total", "type": "double", "required": false},
      {"id": 4, "name": "order_date", "type": "date", "required": true}
    ]
  },
  "partition-spec": {
    "spec-id": 0,
    "fields": [
      {"field-id": 1000, "source-id": 4, "name": "order_date_month", "transform": "month"}
    ]
  },
  "properties": {
    "write.format.default": "parquet",
    "format-version": "2"
  },
  "stage-create": false
}

Load Table: GET /v1/namespaces/{namespace}/tables/{table}

This is the most critical operation: called by every engine before reading or writing:

GET /v1/namespaces/analytics%1Fgold/tables/orders

Response includes:

{
  "metadata-location": "s3://my-bucket/warehouse/.../metadata/v5.metadata.json",
  "metadata": {
    "format-version": 2,
    "table-uuid": "...",
    "location": "s3://...",
    "last-sequence-number": 5,
    "last-updated-ms": 1715700000000,
    "last-column-id": 4,
    "current-schema-id": 0,
    "schemas": [...],
    "partition-specs": [...],
    "default-spec-id": 0,
    "sort-orders": [...],
    "current-snapshot-id": 8027658604211071520,
    "refs": {"main": {"snapshot-id": 8027658604211071520, "type": "branch"}},
    "snapshots": [...]
  },
  "config": {
    "client.factory": "...",
    "s3.access-key-id": "ASIAXXXXXXXX",    // ← CREDENTIAL VENDING
    "s3.secret-access-key": "...",
    "s3.session-token": "..."
  }
}

The config section (and, in newer servers, the storage-credentials list) carries vended credentials: temporary, scoped storage credentials that the engine uses to access data files directly.

Commit Table Update: POST /v1/namespaces/{namespace}/tables/{table}

The atomic commit operation:

POST /v1/namespaces/analytics%1Fgold/tables/orders
Content-Type: application/json

{
  "identifier": {"namespace": ["analytics", "gold"], "name": "orders"},
  "requirements": [
    {
      "type": "assert-ref-snapshot-id",
      "ref": "main",
      "snapshot-id": 8027658604211071520   // ← OPTIMISTIC LOCK
    }
  ],
  "updates": [
    {
      "action": "add-snapshot",
      "snapshot": {
        "snapshot-id": 9135729705312082631,
        "parent-snapshot-id": 8027658604211071520,
        "sequence-number": 6,
        "timestamp-ms": 1715704000000,
        "summary": {"operation": "append", "added-records": "10000"},
        "manifest-list": "s3://my-bucket/warehouse/.../metadata/snap-...avro"
      }
    },
    {
      "action": "set-snapshot-ref",
      "ref-name": "main",
      "type": "branch",
      "snapshot-id": 9135729705312082631
    }
  ]
}

If snapshot-id in requirements doesn’t match the catalog’s current snapshot: HTTP 409 Conflict → client retries with updated state.

View Operations (Spec v1)

REST Catalog v1 includes view support:

POST /v1/namespaces/{namespace}/views      -- Create view
GET  /v1/namespaces/{namespace}/views/{view} -- Load view
HEAD /v1/namespaces/{namespace}/views/{view} -- Check existence
POST /v1/namespaces/{namespace}/views/{view} -- Commit view update
DELETE /v1/namespaces/{namespace}/views/{view} -- Drop view

Error Codes

HTTP CodeMeaning
400 Bad RequestMalformed request
401 UnauthorizedAuthentication failure
403 ForbiddenInsufficient privileges
404 Not FoundNamespace or table doesn’t exist
409 ConflictOptimistic concurrency conflict (retry)
500 Internal Server ErrorCatalog error
503 Service UnavailableCatalog overloaded

Read more on this blog

📚 Go Deeper on Apache Iceberg

Alex Merced has authored three hands-on books covering Apache Iceberg, the Agentic Lakehouse, and modern data architecture. Pick up a copy to master the full ecosystem.

← Back to Iceberg Knowledge Base

Work with Alex