Snowpipe Streaming REST API endpoints

Note

Where possible, use the Snowpipe Streaming SDK instead of the REST API to benefit from automatic batching and simpler integration. Use direct REST when an SDK isn’t suitable for your environment.

The Snowpipe Streaming REST API is designed for lightweight workloads and provides a flexible way to integrate with external applications without using a Snowpipe Streaming SDK.

This reference documents both ingestion modes:

Request headers

The following request headers apply to all the endpoints for the Snowpipe Streaming REST API:

HeaderDescription
AuthorizationAuthentication token
X-Snowflake-Authorization-Token-Type (optional)JWT/OAuth
Content-Type (required when the request has a body)Media type of the request body. Use application/x-ndjson for append-rows requests. Use application/json for open-channel, drop-channel, and bulk channel-status bodies. Use application/x-www-form-urlencoded for the scoped-token exchange.
Content-Encoding (optional)Specifies the compression format of the payload. Supported: gzip, zstd.
User-Agent (optional)Identifies the client application. Recommended format: SnowpipeStreamingSDK/{version} ({platform}) {LANGUAGE}/{language_version} (app={partner-name}). Example: SnowpipeStreamingSDK/1.0.0 (Linux amd64) PYTHON/3.11.0 (app=MyPartnerApp).

Direct REST clients must group rows into newline-delimited JSON (NDJSON), with one JSON object per line, and handle compression themselves. Both Elastic and Named Channel append requests have a 4 MB payload limit (the payload size sent over the network, after compression if used). Batch rows and use ZSTD or Gzip compression to reduce request overhead; bound batch size and elapsed time before sending.

Client requirements

A request that doesn’t meet the following requirements returns an HTTP 4xx error. These errors generally aren’t retryable, except for the cases in Retry policy.

The requirements are:

  • Use the ingest hostname for your account, the documented HTTP method, and the documented request path and query parameters.
  • Include the required headers with valid values, including Authorization and the content type the endpoint expects. When you compress a body, set Content-Encoding to gzip or zstd.
  • Send a valid, unexpired authentication token whose role is authorized for the target account, pipe, and table.
  • Stay within the account, channel, request, and byte-rate limits. For those limits, see Snowpipe Streaming high-performance limitations.

For retry delays, retryable status codes, and Named Channel recovery, see REST API error handling.

Get Hostname

The Get Hostname returns the hostname used to interact with the Snowpipe Streaming REST API. Each account has a unique hostname.

Note

For private connectivity (AWS PrivateLink, Azure Private Link, or Google Cloud Private Service Connect), call this endpoint through your private account URL, and configure private DNS for the returned ingest hostname before you use it for the scoped-token exchange and streaming operations. For discovery commands, DNS configuration, and verification checks shared by SDK, REST, and Kafka clients, see Network connectivity prerequisites.

GET /v2/streaming/hostname

Response:

<ingest-hostname>

The response body is the ingest hostname as plain text, not a JSON object.

Description of the response:

FieldTypeDescription
Response bodyStringThe account-specific streaming ingest hostname.

Exchange Scoped Token

The Exchange Scoped Token returns a security token that can be used to access only the Snowpipe Streaming API-related service. This provides security protection for the customer.

POST /oauth/token

Request:

AttributeRequiredComponentDescription
content_typeYesHeader“application/x-www-form-urlencoded”
grant_typeYesPayload“urn:ietf:params:oauth:grant-type:jwt-bearer”
scopeYesPayloadThe hostname of the account.

Response:

{
  "token": "string"
}

Description of response fields:

FieldTypeDescription
TokenStringThe scoped token.

Elastic Channel endpoints

Elastic Channels don’t require a separate open-channel operation. Send a batch of NDJSON rows directly to a table or to the implicit ELASTIC channel of a pipe:

POST /v2/streaming/data/databases/{databaseName}/schemas/{schemaName}/tables/{tableName}/rows
POST /v2/streaming/data/databases/{databaseName}/schemas/{schemaName}/pipes/{pipeName}/channels/ELASTIC/rows

The table endpoint is only for Elastic Channels. On the first request, Snowflake creates or resolves the managed default pipe named <tableName>-STREAMING. Each streaming pipe includes an implicit ELASTIC channel, which the request uses without a separate open-channel operation. Use the pipe endpoint for an Elastic Channel on a custom pipe with in-flight transformations or pre-clustering.

Elastic request attributes

AttributeRequiredComponentDescription
databaseNameYesURIDatabase name, case-insensitive.
schemaNameYesURISchema name, case-insensitive.
tableName or pipeNameYesURIThe target table for the default pipe, or the custom pipe name.
rowsYesBodyNDJSON rows. The maximum Elastic request payload is 4 MB (the payload size sent over the network, after compression if used).
requestIdNoQuery parameterA UUID that tracks the request. Use the same value on every retry of the same rowset (batch of rows), and generate a new UUID for each distinct rowset.
retryCountNoQuery parameterThe retry attempt number, starting at 0. Increment it for each retry. A value greater than 0 signals that duplicate rows are possible; it doesn’t prove that a duplicate occurred.

Send a non-empty NDJSON rowset. Elastic requests must not include continuationToken, offsetToken, startOffsetToken, or endOffsetToken.

Elastic response and delivery semantics

A successful HTTP 200 response is the durable acknowledgement: Snowflake has durably buffered the request payload. It doesn’t mean that rows are immediately queryable in the target table. Row-level processing errors are persisted to the error table when error logging is enabled.

{
  "message": "OK"
}

Elastic Channels provide at-least-once delivery without an ordering guarantee. Retrying after an ambiguous response can produce duplicate rows. Include a stable event identifier in the row payload and reconcile or deduplicate downstream when duplicates matter.

Elastic append example

export REQUEST_ID=$(uuidgen)

curl -sS -X POST \
  -H "Authorization: Bearer $SCOPED_TOKEN" \
  -H "Content-Type: application/x-ndjson" \
  "https://${INGEST_HOST}/v2/streaming/data/databases/$DB/schemas/$SCHEMA/tables/$TABLE/rows?requestId=$REQUEST_ID&retryCount=0" \
  --data-binary @rows.ndjson | jq .

For a retry of this rowset, reuse REQUEST_ID and increment retryCount. To use a custom pipe, replace the table path with /pipes/$PIPE/channels/ELASTIC/rows.

Named Channel endpoints

Named Channels require explicit channel lifecycle, continuation tokens, and source offset tokens. The following operations support ordered, exactly-once ingestion.

The following diagram shows the Named Channel request flow:

Snowpipe Streaming named-channel REST API flow

Open a Named Channel

The Open Channel operation creates or opens a new channel against a pipe or table. If the channel already exists, Snowflake bumps the client sequencer of the channel and returns the last committed offset token.

PUT /v2/streaming/databases/{databaseName}/schemas/{schemaName}/pipes/{pipeName}/channels/{channelName}

Request:

AttributeRequiredComponentDescription
databaseNameYesURIDatabase name, case-insensitive.
schemaNameYesURISchema name, case-insensitive.
pipeNameYesURIPipe name, case-insensitive.
channelNameYesURIThe name of the channel that you create or re-open, case-insensitive.
offset_tokenNoPayloadString used to set an offset token when opening a channel.
fail_on_uncommitted_rowsNoPayloadBoolean. When true, the server rejects the request with HTTP 409 Conflict (ERR_CHANNEL_HAS_UNCOMMITTED_DATA) if the channel has uncommitted in-flight data. Otherwise, in-flight data is silently discarded and can slow co-located channels on the pipe (default: false). For how to use this field during recovery, see Recover a Named Channel after a REST error.
requestIdNoQuery parameterA universally unique identifier (UUID) used to track requests through the system.

Response:

{
  "next_continuation_token": "string",
  "channel_status": {
    "database_name": "string",
    "schema_name": "string",
    "pipe_name": "string",
    "channel_name": "string",
    "channel_status_code": "string",
    "last_committed_offset_token": "string",
    "created_on_ms": "long",
    "rows_inserted": "int",
    "rows_parsed": "int",
    "rows_error_count": "int",
    "last_error_offset_upper_bound": "string",
    "last_error_message": "string",
    "last_error_timestamp": "timestamp_utc",
    "snowflake_avg_processing_latency_ms": "int"
  }
}

Description of response fields:

FieldTypeDescription
next_continuation_tokenStringAn API-managed token that must be used in the subsequent Append Rows request. The token links a series of calls, ensuring a contiguous, in-order stream of data and maintaining the session state for exactly once delivery.
channel_statusObject

A nested object with the following detailed information about the channel:

  • database_name (String): The name of the database where the pipe is located.
  • schema_name (String): The name of the schema where the pipe is located.
  • pipe_name (String): The name of the specific pipe being used.
  • channel_name (String): The name of the streaming channel.
  • channel_status_code (String): A code that indicates the current status of the channel; for example, “ACTIVE”.
  • last_committed_offset_token (String): The token that represents the last successfully committed offset.
  • created_on_ms (Long): The timestamp, in milliseconds, when the channel was created.
  • rows_inserted (Int): The total number of rows successfully inserted.
  • rows_parsed (Int): The total number of rows parsed.
  • rows_error_count (Int): The total number of rows that encountered an error.
  • last_error_offset_upper_bound (String): A token that indicates the upper bound of the offset where the last error occurred.
  • last_error_message (String): The message of the last error that occurred.
  • last_error_timestamp (Long): The timestamp, in milliseconds, of the last error.
  • snowflake_avg_processing_latency_ms (Int): The average processing latency of Snowflake in milliseconds.

Open channel requirements

  • Use the same databaseName, schemaName, pipeName, and channelName for later open, append, drop, and status calls on that channel.
  • Send open requests for one channel name one at a time. Overlapping open calls against the same name are rejected.
  • Store next_continuation_token for the next append and last_committed_offset_token from this response when the open succeeds. For the recovery sequence, see Recover a Named Channel after a REST error.

Append rows to a Named Channel

The Append Rows operation inserts a batch of rows to the given channel.

Send this request to the ingest hostname returned by Get Hostname, not to your Snowflake account hostname. Set the continuationToken query parameter to the next_continuation_token value returned by the Open Channel request for the first request, and to the next_continuation_token value returned by the previous Append Rows response for each later request.

POST /v2/streaming/data/databases/{databaseName}/schemas/{schemaName}/pipes/{pipeName}/channels/{channelName}/rows

Named Channel append workflow

Every Named Channel append request must include a continuation token. Snowflake issues this token, and your client sends it back unchanged. You get the first token when you open the channel. Each successful append returns the token to use for the next append.

  1. Call Open a Named Channel. In the JSON response body, read the top-level next_continuation_token field and save its value in your client.
  2. Send an Append Rows request. Set these query parameters:
    • continuationToken: The saved token value, unchanged.
    • offsetToken: Your source offset for this batch, such as a Kafka offset. This value records exactly-once progress, and a later open-channel response returns it as last_committed_offset_token.
  3. When the append succeeds, read next_continuation_token from the append response body. Replace your saved token with this new value, and discard the old one.
  4. Repeat steps 2 and 3 for each batch.

Send appends for one channel one at a time. Wait for each append response before you send the next append, because the next request needs the token from that response.

The request body must be a non-empty NDJSON rowset, and the payload sent on the network must be 4 MB or smaller. For retries, see REST API retry policy. For Named Channel recovery, see Recover a Named Channel after a REST error.

Request:

AttributeRequiredComponentDescription
databaseNameYesURIDatabase name, case-insensitive.
schemaNameYesURISchema name, case-insensitive.
pipeNameYesURIPipe, case-insensitive.
channelNameYesURIChannel name, case-insensitive.
continuationTokenYesQuery parameterContinuation token from Snowflake. It encapsulates both client and row sequencers. Use next_continuation_token from the latest successful open or append response. See Named Channel append workflow.
offsetTokenNoQuery parameterUser-defined source offset for this request. Set offsetToken on each append so a later open-channel response can return it as last_committed_offset_token. For how this token differs from continuationToken, see Roles of offsetToken and continuationToken.
startOffsetTokenNoQuery parameterOffset token for the first row in the batch.
endOffsetTokenNoQuery parameterOffset token for the last row in the batch.
rowsYesPayloadThe actual data payload to be ingested in NDJSON format. The maximum allowed size for this attribute is 4 MB.
requestIdNoQuery parameterA UUID used to track requests through the system.

Note

The JSON text within the NDJSON payload must strictly conform to the RFC 8259 standard. Each JSON text must be followed by a newline character \n (0x0A). You can also insert a carriage return \r (0x0D) before the newline character.

Response:

{
  "next_continuation_token": "string"
}

Description of response fields:

FieldTypeDescription
next_continuation_tokenstringThe next continuation token from Snowflake, which encapsulates both client and row sequencers. It should be used for inserting the next batch.

Drop a Named Channel

The Drop Channel operation drops a Named Channel and its metadata on the server. Set fail_on_uncommitted_rows to true to reject the drop when uncommitted rows exist. If you leave it false, Snowflake can discard in-flight data.

DELETE /v2/streaming/databases/{databaseName}/schemas/{schemaName}/pipes/{pipeName}/channels/{channelName}

Request:

AttributeRequiredComponentDescription
databaseNameYesURIDatabase name, case-insensitive
schemaNameYesURISchema name, case-insensitive
pipeOrTableNameYesURIPipe or table name, case-insensitive
channelNameYesURIChannel name, case-insensitive
fail_on_uncommitted_rowsNoPayloadBoolean. When true, the server rejects the request with HTTP 409 Conflict (ERR_CHANNEL_HAS_UNCOMMITTED_DATA) if the channel has uncommitted in-flight data. Otherwise, in-flight data is silently discarded and can slow co-located channels on the pipe (default: false).
requestIdNoQuery parameterA UUID used to track requests through the system

Response:

This operation returns a payload with no specific successful response other than the HTTP status code.

Get Named Channel status in bulk

The Bulk Get Channel Status operation returns the status of a channel for a specific client sequencer.

POST /v2/streaming/databases/{databaseName}/schemas/{schemaName}/pipes/{pipeName}:bulk-channel-status

Request:

AttributeRequiredComponentDescription
databaseNameYesURIDatabase name, case-insensitive
schemaNameYesURISchema name, case-insensitive
pipeNameYesURIPipe name, case-insensitive
channel_namesYesPayloadAn array of String channel names that the customer wants to get status for; the names are case-sensitive. For example, {"channel_names":["channel1", "channel2"]}.

Response:

{
  "channel_statuses": {
    "channel1": {
      "channel_status_code": "String",
      "last_committed_offset_token": "String",
      "database_name": "String",
      "schema_name": "String",
      "pipe_name": "String",
      "channel_name": "String",
      "rows_inserted": "int",
      "rows_parsed": "int",
      "rows_errors": "int",
      "last_error_offset_upper_bound": "String",
      "last_error_message": "String",
      "last_error_timestamp": "timestamp_utc",
      "snowflake_avg_processing_latency_ms": "int"
    },
    "channel2": {
      "comment": "same structure as channel1"
    }
    "comment": "potentially other channels"
  }
}

Note

If no requested channel is found in the service, the response payload doesn’t have an entry for that channel within the channel_statuses object.

Description of channel_statuses fields for each channel:

FieldTypeDescription
channel_status_codeStringIndicates the status of the channel.
last_committed_offset_tokenStringLatest committed offset token.
database_nameStringThe name of the database that the channel belongs to.
schema_nameStringThe name of the schema that the channel belongs to.
pipe_nameStringThe name of the pipe that the channel belongs to.
channel_nameStringThe name of the channel.
rows_insertedintA count of all rows inserted into this channel.
rows_parsedintA count of all rows parsed, but not necessarily inserted into this channel.
rows_errorsintA count of all rows that experienced errors when inserted into this channel and were therefore rejected.
last_error_offset_upper_boundStringThe upper bound for an ingestion error. The error will be located at or before this committed offset token.
last_error_messageStringA human readable message corresponding to the latest error code for that channel, with sensitive customer data redacted.
last_error_timestamptimestamp_utcTimestamp at the time when the last error occurred.
snowflake_avg_processing_latency_msintAverage end-to-end processing time for this channel.

Error response structure

The Snowpipe Streaming REST APIs return a JSON payload for error responses. This structure provides actionable information for both automated error handling and human analysis. For which status codes to retry, how to refresh authentication, and how to recover a Named Channel, see REST API error handling.

The response payload has the following structure:

{
  "code": "...",
  "message": "..."
}

Response fields

FieldTypeDescription
CodeStringA stable, programmatic error code. This value can be used for automated error handling and logging. For example, an application’s logic can check for a specific code to trigger a predefined action.
MessageStringA human-readable message that describes the error. This message is subject to change and shouldn’t be used for automated parsing.

Example

The following example shows an error response you might receive:

{
  "code": "STALE_CONTINUATION_TOKEN_SEQUENCER",
  "message": "Channel sequencer in the continuation token is stale. Please reopen the channel"
}

This example shows the response for an attempt to use a continuation token with a stale channel sequencer. The code provides a clear, machine-readable identifier for the error, and the message offers a helpful, descriptive text for a user.