Integration GuidesDocs
Coverage MatrixDocumentationChange LogLog InContact Us
Integration Guides

Push Feeds

With RESTful endpoints you make a request each time you want new data. Push feeds instead open a connection with a single call and deliver continuous live updates: no pull frequency to manage, just listen for match updates as they happen.

🔐

Access

Tennis Push feeds are available for Sportradar Realtime customers and are not part of the self-issued trial in your account. Reach out to a sales representative for trial access.



Push and RESTful Together

Each Push update is a payload that mirrors the structure of a similar RESTful feed. Payloads are delivered for changes to events and statistics, not for match status updates.

Push complements the RESTful API rather than replacing it. Push has no stateful session: there is no memory of or access to previously sent data. After a disconnection, use the corresponding RESTful feed to recover anything missed (for Push Events, catch up from Live Timelines or Sport Event Timeline). Push feeds also do not carry every data point the RESTful API serves, so a complete integration uses both.



Making a Request

Each Push feed starts with a URL request, like any other endpoint:

curl -L -X GET 'https://api.sportradar.com/tennis/{access_level}/{version}/stream/events/subscribe' \
  -H 'x-api-key: {your_api_key}'

The call opens an HTTP redirect connection that returns all Push data for the requested feed. There are no restrictions on the number of open connections: filter requests to individual matches and close connections as matches complete, or keep a single unfiltered connection open indefinitely.


Filtering

By default a Push feed delivers everything: an unfiltered Push Events connection streams every tennis match in progress. Add query strings to narrow the stream, for example to one match:

curl -L -X GET 'https://api.sportradar.com/tennis/trial/v3/stream/events/subscribe?&format=json&sport_event_id=sr:sport_event:13468929' \
  -H 'x-api-key: {your_api_key}'

See the endpoint pages for the full query string syntax.


Technical Requirements

To accept data from Push feeds, ensure your application can:

  • Follow an HTTP redirect, or use the location provided in the feed's header within one minute of your initial request.
  • Accept HTTP data transfer encoded as chunked.


Payloads

Push is delivered by HTTP streaming with chunked encoding: a long HTTP response whose chunks you act on as they arrive. When no new information is available, a heartbeat message is sent every 5 seconds to keep the connection active. Messages are delivered in JSON format only.

A stream interleaves heartbeats with payloads. Here is an excerpt showing a heartbeat, a period_score payload with its metadata, and further heartbeats:

}{
   "heartbeat":{
      "from":1738689527,
      "interval":5,
      "to":1738689532,
      "type":"events",
      "package":"tennis-v3"
   }
}{
   "payload":{
      "sport_event_status":{
         "status":"live",
         "match_status":"3rd_set",
         "home_score":1,
         "away_score":1,
         "period_scores":[
            {
               "home_score":4,
               "away_score":6,
               "type":"set",
               "number":1
            },
            {
               "home_score":6,
               "away_score":1,
               "type":"set",
               "number":2
            },
            {
               "home_score":5,
               "away_score":2,
               "type":"set",
               "number":3
            }
         ],
         "game_state":{
            "home_score":0,
            "away_score":0,
            "serving":"away",
            "last_point_result":"receiver_winner",
            "tie_break":false
         }
      },
      "event":{
         "id":485308,
         "type":"period_score",
         "time":"2025-02-04T17:18:50+00:00",
         "period":"3",
         "competitor":"home",
         "home_score":5,
         "away_score":2,
         "server":"home",
         "result":"server_won"
      }
   },
   "metadata":{
      "format":"json",
      "sport_event_id":"sr:sport_event_id:57798949",
      "event_id":"period_score",
      "channel":"tennis",
      "competition_id":"sr:competition:35428",
      "sport_id":"sr:sport:5",
      "season_id":"sr:season:126111"
   }
}{
   "heartbeat":{
      "from":1738689532,
      "interval":5,
      "to":1738689537,
      "type":"events",
      "package":"tennis-v3"
   }
}{
   "heartbeat":{
      "from":1738689537,
      "interval":5,
      "to":1738689542,
      "type":"events",
      "package":"tennis-v3"
   }
}{
   "heartbeat":{
      "from":1738689542,
      "interval":5,
      "to":1738689547,
      "type":"events",
      "package":"tennis-v3"
   }

Disconnections

If heartbeats stop arriving, or you are disconnected for any reason, reconnect using your same initial request, then backfill from the RESTful counterparts.


Metadata

Each payload carries a metadata object identifying the payload type and its match, competition, sport, and season. Use it to filter and route messages without parsing the full payload:

{
  "metadata": {
    "format": "json",
    "sport_event_id": "sr:sport_event_id:57756307",
    "event_id": "point",
    "channel": "tennis",
    "competition_id": "sr:competition:34320",
    "sport_id": "sr:sport:5",
    "season_id": "sr:season:124107"
  }
}
{
  "metadata": {
    "format": "json",
    "sport_event_id": "sr:sport_event_id:57756307",
    "event_id": "period_score",
    "channel": "tennis",
    "competition_id": "sr:competition:34320",
    "sport_id": "sr:sport:5",
    "season_id": "sr:season:124107"
  }
}


ID Updates

Events in the Push Events stream carry an updated flag set to true when a message creates or revises a previously delivered event. When you receive an event with the same ID as one already consumed, replace the earlier data with the most recent message:

{
  "payload": {
    "sport_event_status": {
      "status": "live",
      "match_status": "2nd_set",
      "home_score": 0,
      "away_score": 1,
      "period_scores": [
        {
          "home_score": 2,
          "away_score": 6,
          "type": "set",
          "number": 1
        },
        {
          "home_score": 2,
          "away_score": 5,
          "type": "set",
          "number": 2
        }
      ],
      "game_state": {
        "home_score": 15,
        "away_score": 40,
        "serving": "away",
        "last_point_result": "ace",
        "tie_break": false,
        "point_type": "match"
      }
    },
    "event": {
      "id": 1961571099,
      "type": "point",
      "time": "2025-02-04T17:23:26+00:00",
      "competitor": "away",
      "updated": true,
      "updated_time": "2025-02-04T17:23:33+00:00",
      "home_score": 15,
      "away_score": 40,
      "server": "away",
      "result": "ace"
    }
  }
}


Code and Data Samples

Sample consumers in Ruby and Java are below; both print the stream to STDOUT. For Java, a Stream Client is also available. In the Java sample, replace "URL GOES HERE" with the desired Push feed URL.

require 'httpclient'

module Sportradar
  module HTTP
    module Stream
      class Client
        attr_reader :url, :logger

        def initialize(url, api_key, logger)
          @url = url
          @logger = logger
          @api_key = api_key
          @client = ::HTTPClient.new(agent_name: 'SportsData/1.0')
        end

        def start
          @thread ||= Thread.new do
            logger.debug "Starting loop"
            headers = {
              'x-api-key' => @api_key
            }
            @client.get_content(url, header: headers, follow_redirect: true) do |chunk|
              @publisher.publish(::JSON.parse(chunk)) if @publisher
            end
            logger.debug "finished loop"
          end
        end

        def stop
          @thread.terminate if @thread
        end
      end
    end
  end
end
package com.sportradar.http.stream.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class StreamClient {

    private Thread streamThread;
    private volatile boolean running = false;
    private String apiKey;

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public void stream(String serviceUrl, Handler handler) {
        running = true;
        streamThread = new Thread(() -> {
            try {
                URL url = new URL(serviceUrl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setRequestProperty("User-Agent", "SportsData/1.0");

                if (apiKey != null && !apiKey.isEmpty()) {
                    connection.setRequestProperty("x-api-key", apiKey);
                }

                try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                    String line;
                    while (running && (line = reader.readLine()) != null) {
                        if (!line.trim().isEmpty()) {
                            handler.handle(line);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        streamThread.start();
    }

    public void terminate() {
        running = false;
        if (streamThread != null) {
            streamThread.interrupt();
        }
    }
}

Complete data samples of a full match are available for Push Events and Push Statistics.



Endpoint Docs

Visit the links below for syntax structure, data samples, and data dictionaries:

  • Push Events: detailed, real-time information on every live match event.
  • Push Statistics: detailed, real-time match stats for all live matches.

Did this page help you?