Using Plaid to display bank account balance

I haven’t seen a writeup of this elsewhere so thought I’d share! I set up a sensor using Plaid APIs to display a bank account balance in HA:

This is completely free (using the Limited Production environment) if you want to use it for a small personal setup like this.

Here are roughly the steps involved, which do require some development skills, or at least willingness to act like a developer :wink:. It also requires some patience, as you will need to contact support and wait for approval for certain steps.

(Sorry if I missed a step, since I’m writing this up after the fact.)

  1. Sign up for Plaid. Depending on which financial institutions you want to link with, you may need to need to also request approval for Production access and fill out some additional forms/questionnaires for OAuth — even if you are going to be staying within the free Limited Production API calls. From the launch checklist:

    Complete your application profile and company profile, which are required to access certain institutions that use OAuth-based connections.

    You can view the status for your OAuth access here.

  2. EDIT 2025/1/18: apparently Hosted Link no longer requires a support request!

    Create a support ticket asking to enable Hosted Link. This is a fully-hosted version of the Plaid authentication flow that allows you to link your bank accounts without deploying any software (without Hosted Link you would need to provide a URL for the normal Link flow to redirect back to). You will have to describe to the support team what you’re planning to build, at least in enough detail to convince them you’re not a scammer.

    You can probably proceed without Hosted Link if you have the skills to implement a regular Link flow.

  3. Send a POST /link/token/create with your client_id and Production secret, and include "hosted_link": {} to enable Hosted Link:

    curl -X POST "https://production.plaid.com/link/token/create" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
      "client_id": "YOUR_CLIENT_ID_HERE",
      "secret": "YOUR_PRODUCTION_SECRET_HERE",
      "language": "en",
      "client_name": "Plaid Test App",
      "country_codes": ["US"],
      "products": ["transactions"],
      "hosted_link": {}
    }'
    

    You should get a response that looks like this:

    {
      "expiration": "2024-06-15T04:09:15Z",
      "hosted_link_url": "https://secure.plaid.com/hl/...",
      "link_token": "link-production-...",
      "request_id": "..."
    }
    

    Take note of the link_token and hosted_link_url values. If the hosted_link_url is missing, you did not complete the previous step!

  4. Copy and paste the hosted_link_url into your browser. This will take you through the regular Plaid linking flow that you see when linking accounts on other websites. Connect the bank account(s) you wish to display in HA.

  5. Once you’ve successfully linked your account, call POST /link/token/get with your link_token:

    curl -X "POST" "https://production.plaid.com/link/token/get" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
      "client_id": "YOUR_CLIENT_ID_HERE",
      "secret": "YOUR_PRODUCTION_SECRET_HERE",
      "link_token": "link-production-THE_LINK_TOKEN_YOU_RECEIVED_ABOVE"
    }'
    

    This will return a lengthy response from which you need to extract the public_token:

    {
      "created_at": "2024-06-15T03:39:14Z",
      "expiration": "2024-06-15T04:09:15Z",
      "link_sessions": [
        {
          "events": [
            ...lots of stuff here...
          ],
          "finished_at": "2024-06-15T03:42:06.285807862Z",
          "link_session_id": "...",
          "results": {
            "item_add_results": [
              {
                "accounts": [
                  {
                    "name": "My account",
                    ...
                  }
                ],
                "institution": {
                  "institution_id": "ins_56",
                  "name": "Chase"
                },
                "public_token": "public-production-THIS_IS_THE_IMPORTANT_PART!!!"
              }
            ]
          }
        }
      ],
      ...
    }
    
  6. Call POST /item/public_token/exchange to convert the public token into an access token:

    curl -X "POST" "https://production.plaid.com/item/public_token/exchange" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
      "client_id": "YOUR_CLIENT_ID_HERE",
      "secret": "YOUR_PRODUCTION_SECRET_HERE",
      "public_token": "public-production-THE_PUBLIC_TOKEN_YOU_RECEIVED_ABOVE"
    }'
    

    This will return an access_token, which is what Home Assistant will use to retrieve your account balance:

    {
      "access_token": "access-production-...",
      "item_id": "...",
      "request_id": "..."
    }
    
  7. Edit your secrets.yaml and add the following:

    plaid_client_id: YOUR_CLIENT_ID_HERE
    plaid_secret: YOUR_PRODUCTION_SECRET_HERE
    plaid_account_balance_get_payload: |
      {"access_token":"access-production-THE_ACCESS_TOKEN_YOU_RECEIVED_ABOVE"}
    
  8. Finally, edit your configuration.yaml to add a RESTful sensor(s) using these secrets and /accounts/balance/get EDIT 2024/7/23: I believe /accounts/get is completely free and equally satisfactory for this use case:

    rest:
      - scan_interval: 43200 # 12 hours
        resource: https://production.plaid.com/accounts/get
        method: POST
        headers:
          Content-Type: application/json
          PLAID-CLIENT-ID: !secret plaid_client_id
          PLAID-SECRET: !secret plaid_secret
        payload: !secret plaid_account_balance_get_payload
        sensor:
          - device_class: monetary
            name: My bank account current balance
            unique_id: my_bank_account.current_balance
            unit_of_measurement: USD  # change as necessary :)
            value_template: |
              {{
                (value_json.accounts | first).balances.current
              }}
            # Note: for extra sanity-checking, you can also use
            # `value_json.accounts | selectattr('account_id', 'equalto', '...') | first`
            # if you know which account_id refers to the account you want.
    
          - device_class: monetary
            name: My bank account available balance
            unique_id: my_bank_account.available_balance
            unit_of_measurement: USD  # change as necessary :)
            value_template: |
              {{
                (value_json.accounts | first).balances.available
              }}
    

If everything went smoothly you should have new sensor entities in HA :slight_smile: :tada: (If you want to link more than one financial institution, you’ll need to repeat steps 3-8.)

I imagine that this could be turned into a blueprint or integration or something, kind of like how the Google Assistant setup flow works — but I’m pretty new to customizing HA so I didn’t try to go down that route yet, I was just happy to get it working for myself. I’d be curious if anyone knows more about how to make this reusable or easier for others to set up!

3 Likes

Thank you so much for this guide! It works beautifully…

Some things I would like to add:
You no longer need to request access to a hosted-link. Here is an email from Plaid.

Thanks for reaching out to Plaid Support!

Previously, Hosted Link was enabled via Plaid Account Managers; it is no longer necessary to request that your Account Manager enable Hosted Link. If your account was enabled for Hosted Link via your Account Manager, the hosted_link_url will be present in the response for all /link/token/create requests, regardless of whether you include a hosted_link object.

To start with Hosted Link, call /link/token/create and include a hosted_link object in the request. The hosted_link object can be an empty object {} , or it can contain any of the hosted_link configuration fields. As long as you include the hosted_link object in your request, the hosted_link_url will be present in the response.

For more information, please refer to our docs here: Link - Hosted Link | Plaid Docs

Step 3: To generate my hosted link, I had to add the following to my body:

curl -X POST "https://production.plaid.com/link/token/create" \
     -H "Content-Type: application/json; charset=utf-8" \
     -d '{
  "client_id": "<client>",
  "secret": "<secret>",
  "language": "en",
  "client_name": "Plaid Test App1",
  "country_codes": ["US"],
  "products": ["transactions"],
  "user": {
    "client_user_id": "<id>"
  },
  "hosted_link": {}
}'

I was also having an issue with connecting my Plaid API to my Chase bank account, but it appears that Chase reviews access requests by hand. You can check the status of your app’s registration with the desired bank accounts here.

However, I was able to get my local credit union’s account integrated!

Thanks for all your work and detailed documentation!

1 Like

Thanks for the updates and glad it worked! Incorporated some edits into my original post :slight_smile:

1 Like