Smarkets | Smarkets API Integration for Java Developers

Smarkets is the third major UK betting exchange, and it has a few characteristics that make it worth adding to a multi-exchange trading system. The commission structure is among the lowest in the market — 2% flat. The REST and streaming APIs are well-documented and genuinely modern. And for certain political events and US sports markets, Smarkets often has comparable or superior liquidity to Betfair. If you’ve built Betfair integration in Java, adding Smarkets is a few days’ work — and the incremental edge it provides can be meaningful.

Smarkets API Overview

Smarkets provides two API surfaces:

Authentication uses OAuth2 tokens. API access is available via the developer portal; you’ll need to apply for a trading account with API access.

Authentication

Smarkets uses a session token obtained via login:

@Service
public class SmarketsAuthService {

    private static final String AUTH_URL = "https://api.smarkets.com/v3/sessions/";

    private final RestClient restClient;

    public String login(String username, String password) {
        LoginRequest loginRequest = new LoginRequest(username, password);

        LoginResponse response = restClient.post()
            .uri(AUTH_URL)
            .contentType(MediaType.APPLICATION_JSON)
            .body(loginRequest)
            .retrieve()
            .body(LoginResponse.class);

        return response.token(); // bearer token for subsequent requests
    }
}

Include the token in subsequent requests:

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(token);
headers.setContentType(MediaType.APPLICATION_JSON);

Sessions have limited TTLs; refresh tokens before they expire, or handle 401 responses with automatic re-authentication.

Browsing Events and Markets

public List<SmarketsEvent> getHorseRacingEvents() {
    // List event types
    String response = restClient.get()
        .uri("/v3/events/?type=race&state=upcoming&sort=start_datetime")
        .header("Authorization", "Bearer " + token)
        .retrieve()
        .body(String.class);

    // Parse events from JSON response
    return objectMapper.readValue(response, EventsResponse.class).getEvents();
}

public List<SmarketsMarket> getMarketsForEvent(String eventId) {
    MarketsResponse response = restClient.get()
        .uri("/v3/events/" + eventId + "/markets/")
        .header("Authorization", "Bearer " + token)
        .retrieve()
        .body(MarketsResponse.class);

    return response.getMarkets();
}

The Smarkets API uses string IDs throughout. A market has a contractId per runner — the equivalent of Betfair’s selectionId.

Retrieving Prices

public List<Quote> getPrices(String marketId) {
    QuotesResponse response = restClient.get()
        .uri("/v3/markets/" + marketId + "/quotes/")
        .header("Authorization", "Bearer " + token)
        .retrieve()
        .body(QuotesResponse.class);

    return response.getQuotes();
}

Each Quote contains bid prices (equivalent to Betfair’s available-to-lay from the customer perspective) and offer prices (equivalent to available-to-back). The terminology maps to traditional financial markets — Smarkets was founded with a financial markets background and it shows.

Placing Orders

public OrderResponse placeBackOrder(
        String marketId, String contractId,
        String price, String quantity) {

    // Smarkets uses fractional odds represented as rationals (e.g. "4/1")
    // or decimal expressed as numerator/denominator pairs
    PlaceOrderRequest request = PlaceOrderRequest.builder()
        .marketId(marketId)
        .contractId(contractId)
        .side("buy")             // "buy" = back, "sell" = lay (financial convention)
        .price(price)            // e.g. "4.5" for decimal odds
        .quantity(quantity)      // stake in pence as a string, e.g. "1000" = £10
        .type("limit")
        .build();

    return restClient.post()
        .uri("/v3/orders/")
        .header("Authorization", "Bearer " + token)
        .contentType(MediaType.APPLICATION_JSON)
        .body(request)
        .retrieve()
        .body(OrderResponse.class);
}

Note the buy/sell terminology — consistent with financial markets, opposite to Betfair’s BACK/LAY convention. buy means you want the selection to win; sell means you want it to lose.

WebSocket Streaming

Smarkets provides real-time price updates via WebSocket:

@Component
public class SmarketsStreamClient {

    private static final String STREAM_URL = "wss://stream.smarkets.com/";

    private WebSocketSession session;

    public void connect(List<String> marketIds) throws Exception {
        WebSocketClient client = new StandardWebSocketClient();

        session = client.execute(new TextWebSocketHandler() {
            @Override
            public void handleTextMessage(WebSocketSession session, TextMessage message) {
                processMessage(message.getPayload());
            }

            @Override
            public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
                log.warn("Smarkets stream closed: {}", status);
                scheduleReconnect();
            }
        }, STREAM_URL).get();

        // Subscribe to markets
        SubscribeRequest sub = new SubscribeRequest(marketIds);
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(sub)));
    }

    private void processMessage(String payload) {
        // Parse market update and apply to local state cache
        SmarketsMarketUpdate update = objectMapper.readValue(payload, SmarketsMarketUpdate.class);
        stateCache.apply(update);
    }
}

Multi-Exchange Abstraction

A common adapter interface normalises Smarkets and Betfair behind a shared model:

public interface ExchangeAdapter {

    String exchangeId();

    List<ExchangeMarket> findMarkets(ExchangeMarketFilter filter);

    ExchangePriceView getPrices(String exchangeMarketId);

    ExchangeOrderResult placeOrder(ExchangeOrder order);

    void cancelOrder(String exchangeOrderId);
}

@Component
public class SmarketsAdapter implements ExchangeAdapter {

    @Override
    public String exchangeId() { return "SMARKETS"; }

    @Override
    public ExchangeOrderResult placeOrder(ExchangeOrder order) {
        // Translate ExchangeOrder to Smarkets request
        // Handle buy/sell convention mapping
        String side = order.getSide() == Side.BACK ? "buy" : "sell";
        OrderResponse response = smarketsClient.placeOrder(
            order.getMarketId(), order.getSelectionId(),
            String.valueOf(order.getPrice()), String.valueOf((int)(order.getStake() * 100))
        );
        return ExchangeOrderResult.fromSmarkets(response);
    }
}

The strategy layer works only against ExchangeAdapter. Arbitrage opportunities between Betfair and Smarkets become detectable when both adapters report prices for correlated markets.

Smarkets vs Betfair — Practical Notes

Aspect Smarkets Betfair
Commission 2% flat 2–5% tiered
API style REST + WebSocket REST + TCP streaming
Streaming WebSocket (standard) Custom TCP/ESA protocol
Terminology Financial (buy/sell) Exchange (back/lay)
Liquidity Lower overall, strong in politics/US sports Highest in UK horse racing
Developer docs Good Excellent

ProTips

If you’re looking for a Java contractor who knows this space inside out, get in touch.