Accessing the TD Ameritrade APIs from Node.js

The TD Ameritrade APIs allow developers to create applications that perform authorized actions on brokerage accounts. These actions include executing trades, getting account balances and getting stock quotes. Therefore, you can use these capabilities to implement automated trading strategies. In this post, I will show how to use Node.js for integrating with the TD Ameritrade Authentication API to grant OAuth 2.0 based access to an application. To follow this example, make sure you meet the following two prerequisites.

  • Install Node.js for your operating system (I used macOS X).
  • Have an existing TD Ameritrade brokerage account or open a new one (for account access and trades).

Register for a new TD Ameritrade Developer Account

For gaining access to the TD Ameritrade APIs, you need to use the below link to register and to setup a new TD Ameritrade Developer account. Complete the registration form and click on the button Create new account to complete the process.

TD Ameritrade for API developers - Register for a new developer account
TD Ameritrade for API developers – Register for a new developer account

After that, log in to the developer account and navigate to APIs to view a list of all available TD Ameritrade APIs. All of these APIs require authorization according to the OAuth 2.0 protocol.

TD Ameritrade for API developers - View list of available APIs
TD Ameritrade for API developers – View list of available APIs

Create a new App on TD Ameritrade for Developers

To make authorized calls to the TD Ameritrade APIs, you need to create a new App. As a result, you will get a OAuth 2.0 user ID that is used for application authorization. Navigate to My Apps in the main menu and click on the Add a new App button.

TD Ameritrade for API developers - View My Apps
TD Ameritrade for API developers – View My Apps

Complete the below form to create a new App. The callback URL is used by the OAuth 2.0 authorization scheme for receiving a code, which can then be used to obtain an access token for making authorized calls to the TD Ameritrade APIs.

  • App Name: Unique name of your app.
  • Callback URL: URL of the local web application that will receive the OAuth 2.0 code. It needs to be a secure URL.
  • OAuthUser ID: User id to use for OAuth 2.0 authorization scheme.
TD Ameritrade for API developers - Add App form to create new app
TD Ameritrade for API developers – Add App form to create new app

The new App is now listed under My Apps. Here, the app name is td-api-tester, the OAuth 2.0 user ID is ferdinand1 and the callback URL is https://localhost:8443. Make sure to wait until the new app has been approved before continuing.

TD Ameritrade for API developers - View new app under MyApps tab
TD Ameritrade for API developers – View new app under MyApps tab

You can click on the app name to view its details, including the keys and the configured callback URL. Furthermore, you can also delete the app on this page.

TD Ameritrade for API developers - View new app keys and details
TD Ameritrade for API developers – View new app keys and details

Create a Self-Signed SSL Certificate for Local Callback URL

The TD Ameritrade OAuth 2.0 authorization scheme requires a secure callback URL, that is it must be reachable via HTTPS. A self-signed certificate suffices and you can create one in a few steps with the OpenSSL toolkit. Install the program then create a new folder that will contain your SSL certificate files. After that, run the below command inside that folder.

openssl req -newkey rsa:2048 -nodes -subj ‘/CN=localhost’ -keyout key.pem -x509 -days 365 -out cert.pem

Running the command should produce the output shown below and two new files, namely key.pem and cert.pem.

Generating a 2048 bit RSA private key
……….++
.+++
writing new private key to ‘key.pem’

If needed, you can view the certificate details by running the keytool command.

keytool -printcert -file cert.pem

Create Node.js Web Application for Receiving OAuth 2.0 Access Token

The source code of the Node.js web application is shown below. I saved it in a file called MyApp.js, in the same folder as the two SSL certificate files. This Node.js program does the following:

  1. Load the SSL certificate and private key from the specified *.pem files.
  2. Start the secure web server using the generated SSL credentials.
  3. Create handler for HTTPS GET requests to read the OAuth 2.0 code from the URL query string.
  4. Create POST request to use the OAuth 2.0 code to retrieve an access token. See documentation for this API call at https://developer.tdameritrade.com/authentication/apis/post/token-0.
  5. Send the POST request to the TD Ameritrade Authentication API and parse the response.
  6. Send HTML formatted response to the browser to show the OAuth 2.0 code and the access token that was received.
var fs = require('fs');
var https = require('https');
var request = require('request');
//
// [1] Load SSL certificate and private key from files
//
var privateKey  = fs.readFileSync('key.pem', 'utf8');
var certificate = fs.readFileSync('cert.pem', 'utf8');
var credentials = {key: privateKey, cert: certificate};

var express = require('express');
var app = express();
//
// [2] Start a secure web server and listen on port 8443
//
var httpsServer = https.createServer(credentials, app);
console.log("Listening on port 8443...");
httpsServer.listen(8443);
//
// [3] Handle HTTPS GET requests at https://localhost:8443
//
app.get('/', function(req, res){
    console.log('New request');

    let httpStatusCode = undefined;
    let httpErrorMsg = undefined;
    let oAuthCode = req.query.code; // get the OAuth 2.0 code from the request URL
    let oAuthReply = undefined;
    // 
    // [4] POST request for obtaining OAuth 2.0 access token with code
    //
    var options = {
        url: 'https://api.tdameritrade.com/v1/oauth2/token',
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        form: {
            'grant_type': 'authorization_code',
            'access_type': 'offline',
            'code': oAuthCode, 
            'client_id': '[email protected]',
            'redirect_uri': 'https://localhost:8443'
        }
    }
    // 
    // [5] Make POST request
    //
    request(options, function(error, response, body) {
        httpStatusCode = (response === undefined) ? 0 : response.statusCode;
        httpErrorMsg = error;
        css = "style=\"overflow-wrap: break-word; width: 800px;\"";

        if (response.statusCode == 200) {
            oAuthReply = JSON.parse(body);
        }
        //
        // [6] Return view, showing the OAuth 2.0 code and access token
        //
        let html = 
        "<html><body style=\"font-family: monospace;\"><table>" +
          "<tr><td width=\"150\">Status</td><td>" + httpStatusCode + "</td></tr>" +
          "<tr><td>OAuth 2.0 Code</td><td><div " + css + ">" + oAuthCode + "</div></td></tr>" +
          //"<tr><td>OAuth 2.0 Token</td><td><div " + css + ">" + oAuthReply.access_token + "</div></td></tr>" +
          "<tr><td>Full Response</td><td><div " + css + ">" + JSON.stringify(oAuthReply, null, 4) + "</div></td></tr>" +
        "</table></body></html>";

        res.send(html);
    });

    function errorHandler (err, req, res, next) {
        res.status(500)
        res.render('error', { error: err })
    }
});

Run the web server and the application with the command node MyApp.js, executed from a terminal window.

node MyApp.js
Listening on port 8443…

Giving Your App Permission to Access a TD Ameritrade Account

You must use an authorization URL matching the below pattern for obtaining an OAuth 2.0 access token. Insert your redirect URI and client ID and open this URL in a browser. The client ID must be in upper case and appended by @AMER.OAUTHAP.

https://auth.tdameritrade.com/auth?response_type=code&redirect_uri=<REDIRECT_URI>&client_id=<CLIENT_ID>

Most importantly, the URL query string parameters need to be encoded using HTML URL encoding, so that the final URL looks like the one below.

When opening the URL in a browser, you will be prompted to log in to a TD Ameritrade brokerage account.

TD Ameritrade for API developers - Login to brokerage account and link to app
TD Ameritrade for API developers – Login to brokerage account and link to app

On the next page, click on Allow to grant permissions to your app so that it can access your TD Ameritrade brokerage account via the APIs.

TD Ameritrade for API developers - Grant persmission to app to access brokerage account
TD Ameritrade for API developers – Grant persmission to app to access brokerage account

After that, the page will redirect to your callback URL and pass the authorization code in the URL. As a result of using a self-signed SSL certificate here, the browser will show a security warning. In Chrome, click on the Advanced button and then on Proceed to localhost (unsafe) to continue.

TD Ameritrade for API developers - Redirection to OAuth 2.0 callback URL
TD Ameritrade for API developers – Redirection to OAuth 2.0 callback URL

The next page shows the HTML output of the Node.js web application. More importantly, the authorization code from the URL was used to obtain an access- and a refresh token.

TD Ameritrade for API developers - OAuth 2.0 authorization via access token
TD Ameritrade for API developers – OAuth 2.0 authorization via access token

Using the OAuth 2.0 Access Token to Make Authorized API Requests

The access token is valid for 30 minutes. In contrast, the long-lived refresh token is valid for 90 days. The refresh token is used to obtain additional access tokens. For a production grade application, the tokens should be stored securely and logic should be implemented to handle token expiration and for obtaining new access- and refresh tokens. You can now use the access token directly or in your application code to make authorized TD Ameritrade API calls for the given brokerage account. For example, the below screen shot shows a request for getting account details such as the balance. Refer to TD Ameritrade’s Accounts and Trading API for more information. Here, I used Google’s Postman tool to make the HTTP GET request. The OAuth 2.0 access token is passed in the header using the key Authorization and value Bearer <access-token>.

TD Ameritrade Accounts and Trade API - Access Account Information with Postman
TD Ameritrade Accounts and Trade API – Access Account Information with Postman

Using the OAuth 2.0 Refresh Token to Get a New Access Token

Once the access token is expired, the API will return a 401 Unauthorized status code. In this case, a new access token must be obtained by with the long-lived refresh token.

TD Ameritrade Authentication API - Expired OAuth 2.0 access token error
TD Ameritrade Authentication API – Expired OAuth 2.0 access token error

To achieve this, make a POST request to https://api.tdameritrade.com/v1/oauth2/token as shown below. Moreover, refer to TD Ameritrade’s Authentication API for additional details.

TD Ameritrade Authentication API - Get OAuth 2.0 access token with refresh token
TD Ameritrade Authentication API – Get OAuth 2.0 access token with refresh token

12 thoughts on “Accessing the TD Ameritrade APIs from Node.js

  1. Shashi Kiran says:

    Good article, some small tips.
    If you encounter the cert.pem could not be created then it is because you did not enter the country, email, location and other details.
    Just type in “no” and use this command as shown. Notice I have just entered “no” to all details that is of no concern for this tutorial.

    $openssl req -x509 -sha256 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
    Generating a 2048 bit RSA private key
    .........................+++
    .+++
    writing new private key to 'key.pem'
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) []:no
    State or Province Name (full name) []:no
    Locality Name (eg, city) []:no
    Organization Name (eg, company) []:no
    Organizational Unit Name (eg, section) []:no
    Common Name (eg, fully qualified host name) []:no
    Email Address []:no
    ==

    This creates the cert.pem and key.pem files in the current folder.

  2. DanArizona says:

    Awesome!!! It’s working for me! I’ve been struggling with the TD Ameritrade documentation for four weeks.Tried both python and node. Thanks from here to New Years. What a relief!

  3. MJ says:

    Great article! Very helpful. I’m hoping to get you to look at my code below, where I try to load options chain. I keep getting http status 401. However, when I follow your example using postman and using my access token, it works fine. Can you see where my syntax is wrong? Thank you.

    //Start Options Post
    app.post("/options", function(req, res) {
    let timeNow = Date.now();
    if ( (timeNow - timeOfTokenAccess) / 1000 > 30 ) {
    reqNewAccessToken();
    }
    var ticker = req.body.ticker;
    const url = 'https://api.tdameritrade.com/v1/marketdata/chains';

    var options = {
    header: { 'Authorization': 'Bearer ' + accessToken },
    url: url,
    method: 'GET',
    form: {
    'symbol': ticker
    }
    }
    request(options, function(error, response, body) {
    let statusCode = response.statusCode;
    console.log(accessToken, ticker);
    if (statusCode == 200) {
    let optionsChain = JSON.stringify(body);
    res.render("options", {ticker: ticker, optionsChain: optionsChain})
    } else {
    res.render("error", {statusCode: statusCode, error: error});
    }
    });
    });
    //End Options Post

    1. Stefan says:

      Hi,

      401 “unauthorized” looks like a problem with how you pass the authorization header. This line: header: { ‘Authorization’: ‘Bearer ‘ + accessToken }, looks like it could be a problem. The quotation marks do not include the accessToken value. Try printing the options variable to make sure it results in the correct header string.

  4. tom says:

    Anyone had any luck getting a post access token using PostMan? I can do receive a token when using the https://developer.tdameritrade.com/authentication/apis/post/token-0 form but when I copy and paste the same values on PostMan I keep on getting this error,


    "error": "Failed to resolve API Key variable request.header.un"

    Here is the relevant screenshot and code that gets generated via PostMan.

    https://imgur.com/a/oUiLoMO

    https://pastebin.com/9KvwAPH6

    Thanks for any direction.

    1. Stefan says:

      Hi Tom,

      I have not tried a POST request yet. Once I do, I’ll let you know if it worked for me. If you figure it out before that, let me know!

      Thx

    1. Stefan says:

      Hi Robert,

      there are different ways of obtaining access tokens. This NodeJS example is one option, showing how to do it programmatically. This approach can also be used as a basis for implementing a trading application. That is, it can be extended to store the tokens in a database and then use them to make authenticated requests for automated trades etc.

  5. Robert Sullivan says:

    I finally got the refresh_token working! I tried standard Java.net classes, OkHttp3 classes, and scribejava for OAuth classes, NONE of which worked. Finally I tried Apache 4.5.9 HttpClient classes and it works! I actually went one level of abstraction beyond and used the Apache Fluent API on top which makes for simpler syntax.

    Content c = org.apache.http.client.fluent.Request.Post("https://api.tdameritrade.com/v1/oauth2/token")
    .addHeader("Content-Type", "application/x-www-form-urlencoded")
    .bodyForm(Form.form()
    .add("grant_type", "refresh_token")
    .add("refresh_token", "I3u15vcKV/ce5LqX7…”)
    .add("client_id", "VZE...") // No need for the @AMER.OAUTHAP
    .build())
    .execute().returnContent();
    String result = c.asString();

    {
    "access_token" : "CzJqQBzTgmEgb3BVpZmwEiMqY7AC...",
    "expires_in" : 1800,
    "token_type" : "Bearer"
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.