SportAPI Documentation
EN
C Product documentationCoupon API
v1
Service & pricing ↗ Get access ↗
Coupon API / cURL

Coupon API — cURL request examples

This page presents a verified flow: authenticate, retrieve a current outcome from Sport Line API, create the complete bet pointer, place a single, and retrieve the created coupon.

Before you begin

Install curl and jq. Define the following values only in the current server-side session:

COUPON_API_BASE_URL='https://YOUR_COUPON_API_DOMAIN'
COUPON_LOGIN='YOUR_LOGIN'
COUPON_PASSWORD='YOUR_PASSWORD'

SPORT_LINE_BASE_URL='https://YOUR_SPORT_LINE_DOMAIN'
SPORT_LINE_PACKAGE='YOUR_SPORT_LINE_API_KEY'

Never commit real values to source control or documentation.

Step 1. Obtain a JWT

LOGIN_RESPONSE=$(curl --silent --show-error --request POST \
  --url "$COUPON_API_BASE_URL/api/partner/login" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc \
    --arg username "$COUPON_LOGIN" \
    --arg password "$COUPON_PASSWORD" \
    '{username:$username,password:$password}')")

TOKEN=$(printf '%s' "$LOGIN_RESPONSE" | jq -er \
  'select(.code == 1) | .body.token')

Do not print $TOKEN to logs. If jq exits with an error, inspect only the safe part of the response:

printf '%s' "$LOGIN_RESPONSE" | jq \
  '{code, error_code, error_message, path}'

See Authentication.

Step 2. Find a sports-line event

Retrieve the current Live menu:

curl --request GET \
  --url "$SPORT_LINE_BASE_URL/v1/menu/live/en" \
  --header "Package: $SPORT_LINE_PACKAGE" \
  --header 'Accept: application/json'

Select sport_id and tournament_id, then retrieve the events:

SPORT_ID='SPORT_ID_FROM_MENU'
TOURNAMENT_ID='TOURNAMENT_ID_FROM_MENU'

curl --request GET \
  --url "$SPORT_LINE_BASE_URL/v1/events/$SPORT_ID/$TOURNAMENT_ID/sub/50/live/en" \
  --header "Package: $SPORT_LINE_PACKAGE" \
  --header 'Accept: application/json'

Read a current game_id from body[].events_list[]. See the menu and events method references.

Step 3. Retrieve an outcome and its odds

GAME_ID='GAME_ID_FROM_EVENTS'

EVENT_RESPONSE=$(curl --silent --show-error --request GET \
  --url "$SPORT_LINE_BASE_URL/v1/event/$GAME_ID/group/live/en" \
  --header "Package: $SPORT_LINE_PACKAGE" \
  --header 'Accept: application/json')

Select the first unblocked outcome without a personal player_id:

SELECTION=$(printf '%s' "$EVENT_RESPONSE" | jq -ec '
  [.. | objects
    | select(
        has("oc_pointer") and
        (.oc_block == false or .oc_block == 0 or .oc_block == null) and
        (.oc_rate | type == "number") and
        (.op_id == null)
      )
  ] | first
')

OC_POINTER=$(printf '%s' "$SELECTION" | jq -r '.oc_pointer')
COEFFICIENT=$(printf '%s' "$SELECTION" | jq -r '.oc_rate')

Inspect the selection without exposing credentials:

printf '%s' "$SELECTION" | jq \
  '{oc_name, oc_group_name, oc_pointer, oc_rate}'

See event and Odds and Bet Groups.

Step 4. Create the complete bet pointer

The sports-line oc_pointer contains the technical game_id|group_id|type_id|rate components, but it is not a complete Coupon API pointer. Add the line type and current oc_rate for Live:

NORMALIZED_POINTER=${OC_POINTER//|/#}
BET_POINTER="live#$NORMALIZED_POINTER#$COEFFICIENT"

The result has this format:

live#game_id#group_id#type_id#rate#coefficient

For Prematch, query the line feed and use the line prefix. Do not submit stale odds: retrieve the selected outcome again immediately before placing the coupon.

See Bet pointer.

Step 5. Place a test single

PLACE_RESPONSE=$(curl --silent --show-error --request POST \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc \
    --arg pointer "$BET_POINTER" \
    '{
      list_bets: [$pointer],
      amount: 10,
      currency: "USD",
      callback_url: null,
      lang: "en",
      mode: "reject",
      mode_type: null,
      multi: false
    }')")

Print only the essential result fields:

printf '%s' "$PLACE_RESPONSE" | jq '{
  code,
  error_code,
  error_message,
  coupons: [.body.coupons[]? | {
    coupon_code,
    amount,
    coef,
    status
  }]
}'

Success requires a successful HTTP response and code = 1. Store body.coupons[].coupon_code as a string because it can contain leading zeroes.

See Coupon placement.

Step 6. Place an accumulator

Prepare at least two current pointers from different events:

BET_POINTER_1='live#GAME_ID_1#GROUP_ID#TYPE_ID#RATE#COEFFICIENT'
BET_POINTER_2='live#GAME_ID_2#GROUP_ID#TYPE_ID#RATE#COEFFICIENT'

Do not combine outcomes from a main event and one of its sub-events. Every list_bets element in an accumulator must belong to a different event.

Submit the pointers with multi: false:

EXPRESS_RESPONSE=$(curl --silent --show-error --request POST \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc \
    --arg pointer1 "$BET_POINTER_1" \
    --arg pointer2 "$BET_POINTER_2" \
    '{
      list_bets: [$pointer1, $pointer2],
      amount: 10,
      currency: "USD",
      callback_url: null,
      lang: "en",
      mode: "reject",
      mode_type: null,
      multi: false
    }')")

Inspect the accumulator structure:

printf '%s' "$EXPRESS_RESPONSE" | jq '{
  code,
  error_code,
  error_message,
  coupons: [.body.coupons[]? | {
    coupon_code,
    amount,
    coef,
    coupon_type,
    events_count,
    status
  }]
}'

A successful response contains one body.coupons item, coupon_type = 2, and events_count = 2. The amount applies to the whole accumulator, while coef is the combined coefficient.

Step 7. Place separate singles with multi

To create separate singles from the same two pointers, submit multi: true:

MULTI_RESPONSE=$(curl --silent --show-error --request POST \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc \
    --arg pointer1 "$BET_POINTER_1" \
    --arg pointer2 "$BET_POINTER_2" \
    '{
      list_bets: [$pointer1, $pointer2],
      amount: 10,
      currency: "USD",
      callback_url: null,
      lang: "en",
      mode: "reject",
      mode_type: null,
      multi: true
    }')")

Inspect the separate coupons and total stake:

printf '%s' "$MULTI_RESPONSE" | jq '{
  code,
  coupon_count: (.body.coupons | length),
  total_amount: ([.body.coupons[].amount] | add),
  coupons: [.body.coupons[] | {
    coupon_code,
    amount,
    coef,
    coupon_type,
    events_count,
    status
  }]
}'

With two pointers, the API returns two singles with different coupon_code values. The full amount applies to every single: with amount = 10, the total stake is 20, not 10.

See Singles, accumulators, and multi.

Step 8. Retrieve the coupon

COUPON_CODE=$(printf '%s' "$PLACE_RESPONSE" | jq -er \
  'select(.code == 1) | .body.coupons[0].coupon_code')

COUPON_RESPONSE=$(curl --silent --show-error --request GET \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/get?coupon_code=$COUPON_CODE" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN")

This method returns one coupon directly in body, not in body.coupons. Print the coupon state and its bet statuses:

printf '%s' "$COUPON_RESPONSE" | jq '
  def coupon_status:
    {"0":"NEW", "2":"WIN", "4":"LOSE", "8":"RETURN", "15":"UPDATE"}
    [tostring] // "UNKNOWN";
  def bet_status:
    {"0":"NET", "1":"WIN", "2":"LOSE", "3":"RETURN", "4":"RECALCULATE",
     "21":"HALF_WIN", "22":"HALF_LOSE", "23":"PUSH"}
    [tostring] // "UNKNOWN";
  .body | {
    coupon_code,
    status,
    status_name: (.status | coupon_status),
    amount,
    coef,
    calculate_coef,
    real_win,
    calculate_date,
    events: [.events_data[] | {
      game_id,
      bet_name,
      status,
      status_name: (.status | bet_status),
      coef,
      calc_coef,
      calculate_score
    }]
  }
' 

Coupon statuses and individual bet statuses use different value tables. For example, 2 means a winning coupon but a losing individual bet. Do not interpret both fields with one shared mapping.

See Retrieving a coupon.

Step 9. Retrieve active coupons

ACTIVE_RESPONSE=$(curl --silent --show-error --request GET \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/active" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN")

printf '%s' "$ACTIVE_RESPONSE" | jq '{
  code,
  count: (.body | length),
  coupons: [.body[] | {
    coupon_code,
    status,
    amount,
    coef,
    coupon_type,
    events_count
  }]
}'

The coupon array is returned directly in body. An empty array is a successful response and means that there are currently no active coupons.

Step 10. Retrieve recently settled coupons

CALCULATED_RESPONSE=$(curl --silent --show-error --request GET \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/calculated?time=120" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN")

printf '%s' "$CALCULATED_RESPONSE" | jq '{
  code,
  count: (.body | length),
  coupons: [.body[] | {
    coupon_code,
    status,
    amount,
    calculate_coef,
    real_win,
    calculate_date
  }]
}'

The time parameter selects a window based on final settlement time in minutes. It is capped at 120 minutes. This method does not return active coupons.

Step 11. Retrieve several known coupons by code

COUPON_CODE_1='FIRST_COUPON_CODE'
COUPON_CODE_2='SECOND_COUPON_CODE'

RESULTS_RESPONSE=$(curl --silent --show-error --request POST \
  --url "$COUPON_API_BASE_URL/api/partner/coupons/results" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --data "$(jq -nc \
    --arg code1 "$COUPON_CODE_1" \
    --arg code2 "$COUPON_CODE_2" \
    '{coupon_ids: [$code1, $code2]}')")

printf '%s' "$RESULTS_RESPONSE" | jq '{
  code,
  count: (.body.coupons | length),
  coupons: [.body.coupons[] | {
    coupon_code,
    status,
    amount,
    real_win,
    coupon_type,
    events_count
  }]
}'

The batch method returns its array in body.coupons. The response can contain fewer coupons and use a different order, so match records by coupon_code, never by array index.

See Active and settled coupons, Querying coupon results, and Statuses and payouts.

Common errors

ResultCauseAction
HTTP 401The JWT is absent or no longer validSign in again once
error_code = 11The pointer is incomplete or malformedCheck the line type and coefficient
error_code = 501The odds changedRefresh the outcome and apply the selected mode rules
error_code = 502The outcome disappearedAsk the user to select another outcome
error_code = 503The outcome is blockedDo not retry before refreshing the line

Do not blindly retry coupon placement after a network timeout. The first request may have succeeded, and a retry can create a duplicate coupon.