gRPC Streaming
gRPC is a high-performance, open-source universal RPC framework. Your application requests events from the Firehose gRPC channel using a server-streaming RPC call, and receives a continuous sequence of Protocol Buffer binary messages.
gRPC supports code generation in several languages, allowing you to start using the API with strongly typed clients without worrying about serialization details.
Regional Endpoints
| Region | Host | Port |
|---|---|---|
| Rest of the World | partners.ciscospaces.io |
443 |
| Europe | partners.ciscospaces.eu |
443 |
| Singapore | partners.ciscospaces.sg |
443 |
All endpoints use TLS. gRPC operates over HTTP/2.
Authentication
Pass the API key as gRPC metadata:
| Metadata Key | Value |
|---|---|
x-api-key |
Your Firehose API key |
The API key must resolve to an activation with the gRPC endpoint type enabled.
Service Definition
service Firehose {
rpc GetEvents(EventsStreamRequest) returns (stream bytes);
}
message EventsStreamRequest {
int32 min_partition = 1;
int32 max_partition = 2;
int64 from_timestamp = 3;
int32 replica_id = 4;
}
Parameters
| Field | Type | Default | Description |
|---|---|---|---|
min_partition |
int32 | 1 | Start of partition range (1–12). |
max_partition |
int32 | 12 | End of partition range (1–12). |
from_timestamp |
int64 | 0 | Epoch milliseconds to resume consuming from. 0 = latest only. |
replica_id |
int32 | 0 | On-prem replica identifier. Not applicable for cloud activations. |
Response Format
Each streamed message is a raw byte array containing a serialized EventRecord Protocol Buffer message. Deserialize each response as EventRecord using the generated protobuf classes.
Proto Schema
Download the full Protocol Buffer schema that defines EventRecord and all event types:
spaces_firehose_spec_v2.0.proto
Use this file to generate client stubs and message classes in your language of choice:
# Python
protoc --python_out=. --grpc_python_out=. spaces_firehose_spec_v2.0.proto
# Java
protoc --java_out=. --grpc-java_out=. spaces_firehose_spec_v2.0.proto
# Go
protoc --go_out=. --go-grpc_out=. spaces_firehose_spec_v2.0.proto
Field Naming
Proto fields use snake_case (e.g., record_uid, event_type, device_location_update). This differs from the JSON format used by HTTP/WebSocket, which uses camelCase.
Keep-Alive
Same behaviour as HTTP — if no events are available for 15 seconds, the server streams an EventRecord with event_type = KEEP_ALIVE.
Error Handling
Errors are returned as gRPC status codes:
| gRPC Status | HTTP Equivalent | Cause |
|---|---|---|
UNAUTHENTICATED |
401 | API key missing or invalid |
PERMISSION_DENIED |
403 | gRPC endpoint not enabled for activation |
FAILED_PRECONDITION |
400 | Invalid replica_id for cloud activation |
RESOURCE_EXHAUSTED |
429 | Rate limit exceeded |
INTERNAL |
500 | Internal server error; connection closed |
On RESOURCE_EXHAUSTED, wait 180 seconds before reconnecting (same as the HTTP Retry-After behaviour).
Client Examples
Python
import grpc
import firehose_pb2
import firehose_pb2_grpc
channel = grpc.secure_channel(
'partners.ciscospaces.io:443',
grpc.ssl_channel_credentials()
)
stub = firehose_pb2_grpc.FirehoseStub(channel)
metadata = [('x-api-key', '<api-key>')]
request = firehose_pb2.EventsStreamRequest(
min_partition=1,
max_partition=12
)
try:
for event_bytes in stub.GetEvents(request, metadata=metadata):
event = firehose_pb2.EventRecord()
event.ParseFromString(event_bytes)
if event.event_type == firehose_pb2.KEEP_ALIVE:
continue
print(f"{event.event_type}: {event.record_uid}")
except grpc.RpcError as e:
print(f"gRPC error: {e.code()} - {e.details()}")
Java/Kotlin
val channel = ManagedChannelBuilder
.forAddress("partners.ciscospaces.io", 443)
.useTransportSecurity()
.build()
val metadata = Metadata().apply {
put(Metadata.Key.of("x-api-key", Metadata.ASCII_STRING_MARSHALLER), apiKey)
}
val stub = FirehoseGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata))
val request = EventsStreamRequest.newBuilder()
.setMinPartition(1)
.setMaxPartition(12)
.build()
try {
stub.getEvents(request).forEach { eventBytes ->
val event = EventRecord.parseFrom(eventBytes)
if (event.eventType != EventType.KEEP_ALIVE) {
println("${event.eventType}: ${event.recordUid}")
}
}
} catch (e: StatusRuntimeException) {
println("gRPC error: ${e.status} - ${e.message}")
}
Go
conn, err := grpc.Dial(
"partners.ciscospaces.io:443",
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := pb.NewFirehoseClient(conn)
md := metadata.Pairs("x-api-key", apiKey)
ctx := metadata.NewOutgoingContext(context.Background(), md)
stream, err := client.GetEvents(ctx, &pb.EventsStreamRequest{
MinPartition: 1,
MaxPartition: 12,
})
if err != nil {
log.Fatal(err)
}
for {
eventBytes, err := stream.Recv()
if err != nil {
log.Printf("Stream ended: %v", err)
break
}
event := &pb.EventRecord{}
proto.Unmarshal(eventBytes, event)
if event.EventType != pb.EventType_KEEP_ALIVE {
log.Printf("%s: %s", event.EventType, event.RecordUid)
}
}
See Also
- Event Types — Detailed descriptions of each event
- Connection Management — Keep-alive, reconnection, partitioning, rate limiting
- HTTP Streaming — JSON alternative for simpler integrations