Authentication

Every Openmarkets API is protected by OAuth2. Your application exchanges a client ID and secret for a short-lived bearer token, then sends that token on each request.

Credentials are issued by Openmarkets during onboarding. There is no self-service portal. If you do not have a client_id and client_secret yet, contact us.

Authorisation flow

Client credentials grant

Openmarkets allows your application to issue authenticated requests on behalf of the application itself, using the OAuth2 client credentials grant.

With this grant type your application has no authenticated user context, so requests to endpoints that require a user will be rejected as unauthorised.

Token endpoints

EnvironmentURL
Testhttps://stage-identity.openmarkets.com.au/connect/token
Productionhttps://identity.openmarkets.com.au/connect/token

The request is authenticated with HTTP basic authentication, where the client_id is the username and the client_secret is the password.

Requesting a token

Request the scopes you need as a space-separated list.

curl -X POST 'https://stage-identity.openmarkets.com.au/connect/token' \
  -u '{client_id}:{client_secret}' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=market-data-api%20news-api%20oms-api'
var authUrl = "https://stage-identity.openmarkets.com.au/connect/token";
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";

var postData = "grant_type=client_credentials&scope=market-data-api%20news-api%20oms-api";

var client = new HttpClient();

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes($"{clientId}:{clientSecret}")));

var result = await client.PostAsync(
    new Uri(authUrl),
    new StringContent(postData, Encoding.UTF8, "application/x-www-form-urlencoded"));

var tokenJson = await result.Content.ReadAsStringAsync();
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64')

const response = await fetch('https://stage-identity.openmarkets.com.au/connect/token', {
  method: 'POST',
  headers: {
    Authorization: `Basic ${credentials}`,
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    scope: 'market-data-api news-api oms-api',
  }),
})

const token = await response.json()
import requests

response = requests.post(
    "https://stage-identity.openmarkets.com.au/connect/token",
    auth=(client_id, client_secret),
    data={
        "grant_type": "client_credentials",
        "scope": "market-data-api news-api oms-api",
    },
)

token = response.json()

Token response

The access_token is a signed JWT. Treat it as opaque, send it verbatim, and do not depend on its contents. The sample below is truncated for readability.

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkIzMzk2NDk4RUM2Njc3QkZBMjVFRkVENzA1RUQ5OTQ0IiwidHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwczovL2lkZW50aXR5Lm9wZW5tYXJrZXRzLmNvbS5hdS8iLCJuYmYiOjE3ODgyMTg0OTksImlhdCI6MTc4ODIxODQ5OSwiZXhwIjoxNzg4MjIyMDk5LCJhdWQiOlsibWFya2V0LWRhdGEtYXBpIiwiaHR0cHM6Ly9pZGVudGl0eS5vcGVubWFya2V0cy5jb20uYXUvcmVzb3VyY2VzIl0sInNjb3BlIjpbIm1hcmtldC1kYXRhLWFwaSJdLCJjbGllbnRfaWQiOiJkZW1vLWZyb250ZW5kIiwiY2xpZW50X2FhdCI6IjE3MTAyMDE0MDEiLCJjbGllbnRfYnVzaW5lc3NfaWQiOiIxIn0.NFkQ0cI5C5sWiu23wPso297Ywoi8M2njO1N-4hcQeP70NlJx1BpW87wXpL_SlUR6...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "market-data-api news-api oms-api"
}
FieldDescription
access_tokenThe value used as the bearer token to authorise requests.
expires_inLifetime of the token in seconds. The example above expires one hour after it was issued.
token_typeThe type of token obtained. Always a bearer token.
scopeThe scopes actually granted, space separated. This can be narrower than what you requested.

Cache the access_token for the duration given by expires_in and reuse it across requests. Rate limits apply to the token endpoint as well as the APIs.

Using the token

Send the token in the Authorization header on every request:

curl 'https://test-oms-api.openmarkets.com.au/accounts/v1' \
  -H 'Authorization: Bearer {access_token}'

API base URLs

The token endpoint above is shared by every API. The APIs themselves are not: they are separate services on separate hosts, and several are on entirely different domains. Send each request to the host for that API.

APITestProduction
Market Datahttps://test-market-data-api.openmarkets.com.au/https://market-data-api.openmarkets.com.au/
Order Management (OMS)https://test-oms-api.openmarkets.com.au/https://oms-api.openmarkets.com.au/
OMS Streaminghttps://test-oms-streams-api.openmarkets.com.au/https://oms-streams-api.openmarkets.com.au/
News v1https://test-news-api.openmarkets.com.au/https://news-api.openmarkets.com.au/
News v2https://test-news-rest-api.openmarkets.com.au/https://news-rest-api.openmarkets.com.au/
Account Openinghttps://stage-api.openmarkets.com.au/https://api.openmarkets.com.au/
Account Managementhttps://stage-api.openmarkets.com.au/https://api.openmarkets.com.au/
Order Advicehttps://stage-api.openmarkets.com.au/https://api.openmarkets.com.au/
Back Office Datahttps://api.uat.openbroker.io/https://api.openbroker.io/
Contract Noteshttps://api.uat.openbroker.io/https://api.openbroker.io/
Portfolio Updateshttps://api.uat.openbroker.io/https://api.openbroker.io/

Error formats differ along the same lines. Market Data, News v1 and Order Management return the Openmarkets error envelope documented on their own error pages. Account Opening returns an RFC 7807 problem document, and the back office APIs return a ConnectError body. Check the OpenAPI reference for the API you are calling.

If you have been given a dedicated sandbox, the correct URL is provided to you as part of onboarding.

Scopes

A token only grants access to the scopes it was issued with. Request every scope your application needs in a single token, or request separate tokens per API.

APIScope
Account Openingaccounts-submissions
Account Managementaccounts-read, accounts-manage
Order Adviceadvice-read
Back Office Databack-office-api
Contract Notesback-office-api
Portfolio Updatesback-office-api
Market Datamarket-data-api
Newsnews-api
News v2news-rest-api
Order Management (OMS)oms-api
OMS Streamingoms-streams-api

Requesting a scope that is not included in your plan returns a 400 invalid_scope error. Contact support if you need a scope added.