<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</title>
    <description>The latest articles on DEV Community by 𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒 (@srbhr).</description>
    <link>https://dev.to/srbhr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F236396%2F1767c256-4b76-4f40-b668-ccda42509f03.png</url>
      <title>DEV Community: 𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</title>
      <link>https://dev.to/srbhr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srbhr"/>
    <language>en</language>
    <item>
      <title>I've created a detailed guide for building on top of Accounting APIs, let me know your thoughts or anything that you want to build.</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Thu, 05 Mar 2026 05:42:44 +0000</pubDate>
      <link>https://dev.to/srbhr/ive-created-a-detailed-guide-for-building-on-top-of-accounting-apis-let-me-know-your-thoughts-or-2ldp</link>
      <guid>https://dev.to/srbhr/ive-created-a-detailed-guide-for-building-on-top-of-accounting-apis-let-me-know-your-thoughts-or-2ldp</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/apideck/accounting-integration-2967" class="crayons-story__hidden-navigation-link"&gt;Accounting Integration&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/apideck"&gt;
            &lt;img alt="Apideck logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F10330%2F83f9e7ec-19ff-4cba-9ea1-8d42e6e4dfb3.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/srbhr" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F236396%2F1767c256-4b76-4f40-b668-ccda42509f03.png" alt="srbhr profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/srbhr" class="crayons-story__secondary fw-medium m:hidden"&gt;
              𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3310708" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/srbhr" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F236396%2F1767c256-4b76-4f40-b668-ccda42509f03.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/apideck" class="crayons-story__secondary fw-medium"&gt;Apideck&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/apideck/accounting-integration-2967" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/apideck/accounting-integration-2967" id="article-link-3310708"&gt;
          Accounting Integration
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/startup"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;startup&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/erp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;erp&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/apideck/accounting-integration-2967" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;6&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/apideck/accounting-integration-2967#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            14 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>api</category>
      <category>backend</category>
      <category>startup</category>
      <category>erp</category>
    </item>
    <item>
      <title>Accounting Integration</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Thu, 05 Mar 2026 05:42:01 +0000</pubDate>
      <link>https://dev.to/apideck/accounting-integration-2967</link>
      <guid>https://dev.to/apideck/accounting-integration-2967</guid>
      <description>&lt;p&gt;Your finance team didn't sign up to be data-entry clerks. Neither did your customers.&lt;/p&gt;

&lt;p&gt;Yet every day, thousands of businesses manually export transactions from QuickBooks, copy invoice data from Xero, or reconcile payments between three different spreadsheets. It's slow, error-prone, and a waste of skilled people's time.&lt;/p&gt;

&lt;p&gt;Accounting integrations fix this. They connect your software directly to your customers' general ledger, automatically syncing invoices, expenses, payments, and journal entries. No CSV exports. No copy-paste errors. No "we'll reconcile it at month-end."&lt;/p&gt;

&lt;p&gt;This guide covers what accounting integrations actually do, the most common use cases, and how companies like Ramp, Airbase, and BILL have leveraged them to gain a competitive advantage.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.apideck.com/blog/what-is-open-accounting" rel="noopener noreferrer"&gt;open accounting movement&lt;/a&gt; is driving demand for standardized data access across accounting platforms, making integration capabilities a competitive requirement for B2B SaaS products.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an Accounting Integration?
&lt;/h2&gt;

&lt;p&gt;An accounting integration is a connection between your application and an accounting platform like &lt;a href="https://www.apideck.com/connectors/quickbooks" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt;, &lt;a href="https://www.apideck.com/connectors/xero" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;, &lt;a href="https://www.apideck.com/connectors/netsuite" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt;, or &lt;a href="https://www.apideck.com/connectors/sage-intacct" rel="noopener noreferrer"&gt;Sage Intacct&lt;/a&gt;. It allows data to flow between systems, either one-way or bidirectionally, without manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A quick definition, if you're new to this:&lt;/strong&gt; A general ledger (GL) is the master record of all financial transactions in a business. Every transaction gets tagged with a GL code, which is essentially a label that categorizes where the money went: "Travel," "Software Subscriptions," "Office Supplies," and so on. When we talk about syncing to the GL, we mean getting your data into that master record with the right labels attached.&lt;/p&gt;

&lt;p&gt;At a practical level, accounting integrations mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invoices&lt;/strong&gt; created in your billing system appear automatically in your customer's accounting software
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expenses&lt;/strong&gt; logged in your app push directly to the general ledger with the correct GL codes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments&lt;/strong&gt; recorded in one system update the other in real time
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bank transactions&lt;/strong&gt; reconcile automatically instead of requiring manual matching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The alternative is what most businesses still do: export a CSV, open Excel, clean the data, reformat it for the accounting system, import it, and hope nothing gets lost in translation. Multiply that by hundreds of transactions per month, and you've created busywork that produces no value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Accounting Integrations Matter for Your Product
&lt;/h2&gt;

&lt;p&gt;If you're building software that touches money (invoicing, expense management, payroll, procurement, lending, payments), your customers will eventually ask: "Does this connect to our accounting system?"&lt;/p&gt;

&lt;p&gt;The answer determines whether you're a tool they'll actually adopt or one they'll abandon after the free trial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The business case is straightforward:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Faster sales cycles.&lt;/strong&gt; Enterprise buyers have procurement checklists. "Integrates with NetSuite" is often a required line item. Without it, you don't make the shortlist.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower churn.&lt;/strong&gt; Customers who integrate your product into their financial workflows don't leave easily. The switching cost is too high when your app is integrated into their month-end close process.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher contract values.&lt;/strong&gt; Integration features justify premium pricing. Ramp charges more for deeper ERP connections. So does BILL. Customers pay for the time savings.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced support burden.&lt;/strong&gt; Every "how do I export this to QuickBooks?" ticket is a symptom of a missing integration. Build it once, eliminate the ticket category.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market expansion.&lt;/strong&gt; Companies across specific regions, industries, and sizes use certain accounting tools. SMBs gravitate toward QuickBooks and Xero. Mid-market and enterprise prefer NetSuite, Sage Intacct, and Microsoft Dynamics. Offering integrations with the tools your target markets rely on opens doors that would otherwise stay closed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Two Types of Accounting Integrations
&lt;/h2&gt;

&lt;p&gt;Before diving into use cases, it's worth distinguishing between two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Customer-Facing (Product) Integrations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These connect your product to your customers' accounting systems. They're what this guide focuses on—the integrations that become product features, drive sales conversations, and require supporting hundreds of different customer configurations.&lt;/p&gt;

&lt;p&gt;The rest of this guide addresses customer-facing integrations, which present unique challenges around multi-tenancy, authorization, and scale.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Internal Integrations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These connect your company's own systems. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CRM → Accounting:&lt;/strong&gt; When an opportunity closes in Salesforce, automatically create the customer in NetSuite for invoicing
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accounting → BI:&lt;/strong&gt; Sync financial data from Xero to Tableau for executive dashboards
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accounting → Slack:&lt;/strong&gt; Alert the finance channel when invoices are overdue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are typically built with &lt;a href="https://www.apideck.com/blog/top-embedded-ipaas-solutions" rel="noopener noreferrer"&gt;iPaaS tools&lt;/a&gt; (Workato, Tray) or custom scripts. They're one-off connections serving your internal workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  6 Common Use Cases for Accounting Integrations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Expense Management → General Ledger Sync
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Employees submit expenses. Finance approves them. Then someone manually enters each line item into QuickBooks with the correct GL code, department, and tax treatment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Approved expenses push automatically to the accounting system. GL codes mapped by expense category. The finance team reviews exceptions, not every transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; Brex, Expend, ExpenseOnDemand, Ramp, and Airbase all sync expenses directly to QuickBooks, Xero, NetSuite, and Sage Intacct. For corporate card products, this is now table stakes. &lt;a href="https://www.apideck.com/customer-cases/invoice2go-bill-com-integrations" rel="noopener noreferrer"&gt;Invoice2go by BILL&lt;/a&gt; used Apideck's Unified API to speed up accounting integrations across multiple platforms, enabling support for major systems without custom builds. This helped them scale expense and reporting features quickly across over 220,000 customers worldwide.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Below is what the QuickBooks Online integration looks like on Ramp after you set it up.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5Cywz71LzRmqG3TeHJVfLF/184288b5b8c5ec4f6876f134236be411/Screenshot_2024-04-11_at_5.37.51%C3%A2__PM.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5Cywz71LzRmqG3TeHJVfLF/184288b5b8c5ec4f6876f134236be411/Screenshot_2024-04-11_at_5.37.51%C3%A2__PM.png" alt="Below is what the QuickBooks Online integration looks like on Ramp after you set it up. "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Invoicing and Billing → Accounts Receivable
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Your product generates invoices. Your customer's accounting team needs those invoices in their accounts receivable ledger. Right now, they're downloading PDFs and manually creating entries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Invoices created in your system automatically appear in the customer's accounting platform. Payment status syncs back. When the invoice is paid, both systems reflect it without anyone having to touch a keyboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; Stripe's invoicing connects to QuickBooks and Xero. Chargebee syncs subscription invoices to major accounting platforms. Any serious billing tool offers this now. For example, &lt;a href="https://www.apideck.com/customer-cases/roopairs-quickbooks-online-integration" rel="noopener noreferrer"&gt;Roopairs&lt;/a&gt; used Apideck's Unified API to launch a QuickBooks Online integration quickly, saving roughly 40 developer hours and supporting 30 active customers from day one. The team is now planning expansions to Sage Intacct and NetSuite due to the speed and simplicity of the implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Stripe QuickBooks Online integration guide&lt;/em&gt;&lt;br&gt;
&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/N50tPlVmWMqLOm6agKAua/f3bb43b5ea06b5b897dd20ec2b579f51/Screenshot_2026-01-13_at_23.51.20_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/N50tPlVmWMqLOm6agKAua/f3bb43b5ea06b5b897dd20ec2b579f51/Screenshot_2026-01-13_at_23.51.20_2x.png" alt="Screenshot 2026-01-13 at 23.51.20@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Procurement and AP Automation → Purchase Order Matching
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; A company issues a purchase order. Goods arrive. An invoice comes in. Someone has to manually match the PO, receipt, and invoice (the "three-way match") before approving payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Your procurement system shares PO data with the accounting platform. When invoices arrive, matching happens automatically. Exceptions flag for review; clean matches process straight through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; Coupa, SAP Ariba, and newer players like Zip connect procurement workflows directly to ERPs for automated matching and approval routing. For example, &lt;a href="https://www.apideck.com/customer-cases/derive-erp-accounting-integration" rel="noopener noreferrer"&gt;Derive&lt;/a&gt; used Apideck's Unified API to reduce development time by about 70 percent. They went from sign-up to a live Xero integration in just three weeks and later launched a Workday connection in under 90 days, which helped accelerate sales cycles and expand into enterprise markets more quickly.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coupa Invoice Matching Explained&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/3RBqgwzhrjAA2R5m1RPEnN/50b104fbc6e96cf8389c7b96f118fba2/Screenshot_2026-01-13_at_23.58.44_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/3RBqgwzhrjAA2R5m1RPEnN/50b104fbc6e96cf8389c7b96f118fba2/Screenshot_2026-01-13_at_23.58.44_2x.png" alt="Coupa Integration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Revenue Recognition → Compliance with Accounting Standards
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; SaaS companies with usage-based pricing or multi-element contracts can't just recognize revenue when cash hits the bank. Accounting standards like ASC 606 require recognizing revenue over the service period. In plain English: you have to follow specific rules about &lt;em&gt;when&lt;/em&gt; you're allowed to say you've "earned" money. Calculating this manually across thousands of subscriptions is an audit nightmare.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Your billing system sends contract and usage data to the accounting platform. Revenue schedules are generated automatically based on the rules. Auditors get clean documentation without your team building spreadsheets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; Stripe Revenue Recognition, Chargebee's RevRec module, and dedicated tools like Leapfin pull transaction data and push compliant journal entries to NetSuite or Sage Intacct.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chargebee RevRec Features&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/1OIsgeq1MjjXtwfmzks9dx/2b7b16d7a31839a497c1289cc4d45828/Screenshot_2026-01-14_at_00.08.44_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/1OIsgeq1MjjXtwfmzks9dx/2b7b16d7a31839a497c1289cc4d45828/Screenshot_2026-01-14_at_00.08.44_2x.png" alt="Chargebee RevRec Features"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Embedded Fintech → Real-Time Cash Flow Visibility
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Lending platforms, cash flow tools, and financial dashboards need to see a business's real financial position. Bank feeds show cash movement, but not the full picture: receivables, payables, revenue trends, and outstanding invoices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Pull chart of accounts, invoices, bills, and journal entries from the customer's accounting system. Display real-time financial health. Underwrite loans based on actual accounting data, not uploaded bank statements from three months ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; &lt;a href="https://www.openbankingtracker.com/api-aggregators/plaid" rel="noopener noreferrer"&gt;Plaid&lt;/a&gt; handles banking data; accounting integrations from providers like Codat and Apideck fill in the rest. Lenders like Clearco and Pipe use accounting data to underwrite in hours instead of weeks.&lt;/p&gt;

&lt;p&gt;For a technical deep-dive on this pattern, see &lt;a href="https://www.apideck.com/blog/using-accounting-apis-for-smart-lending-decisions" rel="noopener noreferrer"&gt;Using Accounting APIs for Smart Lending Decisions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Plaid API Docs&lt;/em&gt; &lt;br&gt;
&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/4N7Qk2Xy05BDl45nj88tLy/51a18107db9d04b5eae121324e8eb0ef/Screenshot_2026-01-14_at_00.19.11_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/4N7Qk2Xy05BDl45nj88tLy/51a18107db9d04b5eae121324e8eb0ef/Screenshot_2026-01-14_at_00.19.11_2x.png" alt="Plaid API Docs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. AI and Machine Learning → Financial Intelligence
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; You want to offer AI-powered features: budget predictions, anomaly detection, and cash flow forecasting, but your models need comprehensive financial data from each customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The integration:&lt;/strong&gt; Pull balance sheets, income statements, transaction histories, and accounts receivable aging from customers' accounting systems. Feed this data to your ML models for personalized insights. As new transactions flow in, models update automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who does this well:&lt;/strong&gt; Runway, Jirav, and Mosaic pull accounting data to power FP&amp;amp;A automation. Fintech platforms use it to build credit models that outperform traditional underwriting.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Runway Fintech Platform&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/ri7m0E6ZiXCO8bElv0CGE/d1f57078c78bec8e91857c962c0b2739/Screenshot_2026-01-14_at_00.10.41_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/ri7m0E6ZiXCO8bElv0CGE/d1f57078c78bec8e91857c962c0b2739/Screenshot_2026-01-14_at_00.10.41_2x.png" alt="Runway"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Companies Build Accounting Integrations
&lt;/h2&gt;

&lt;p&gt;This is where product decisions become engineering reality. You have three paths:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Build Direct Integrations
&lt;/h3&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5hZHxvvvRpIJeSslAJecUa/9bda6a322d00b38b3cbfc0541fb99568/Mermaid_Chart_-_Create_complex__visual_diagrams_with_text.-2026-01-11-185243.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5hZHxvvvRpIJeSslAJecUa/9bda6a322d00b38b3cbfc0541fb99568/Mermaid_Chart_-_Create_complex__visual_diagrams_with_text.-2026-01-11-185243.png" alt="multiple-integrations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect to each accounting platform's API individually. QuickBooks Online has a REST API. Xero uses OAuth 2.0. NetSuite offers both SOAP and REST. Sage has &lt;a href="https://www.apideck.com/blog/the-sage-api-playbook-why-sage-cloud-is-not-one-api" rel="noopener noreferrer"&gt;several APIs, depending on which Sage product you're targeting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Full control over the integration. Access to every feature the platform offers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; Each integration is a significant investment. Industry estimates indicate that a single integration requires 150+ engineering hours to build and 300+ hours annually to maintain, with total costs ranging from $10,000 to $50,000 per integration per year, including engineering time, customer success support, and ongoing maintenance. Supporting 10 accounting systems means multiplying that investment tenfold. Teams routinely estimate six weeks and ship in three months, as auth flows alone can derail timelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The maintenance reality:&lt;/strong&gt; Accounting APIs change. QuickBooks pushes updates. Xero deprecates endpoints. NetSuite releases new versions. When a sync breaks on the first of the month (the worst possible timing for any finance team), someone on your team is firefighting instead of building features. Direct integrations require ongoing "on-call" capacity that most teams underestimate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Use a Unified API
&lt;/h3&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/NyNWfDtAshr8iXXuLpEWs/038bb8e0a430ca8cf75c28b20bb525e6/Mermaid_Chart_-_Create_complex__visual_diagrams_with_text.-2026-01-11-190250.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/NyNWfDtAshr8iXXuLpEWs/038bb8e0a430ca8cf75c28b20bb525e6/Mermaid_Chart_-_Create_complex__visual_diagrams_with_text.-2026-01-11-190250.png" alt="unified api managed integration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A unified API provider normalizes multiple accounting platforms into a single integration. Think of it as a universal translator: your system speaks one language, and the provider handles the dialects of QuickBooks, Xero, NetSuite, and the rest.&lt;/p&gt;

&lt;p&gt;You build once; they maintain the connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Launch in weeks instead of months. One data model to learn. One auth flow to implement. Coverage across platforms without multiplying your engineering investment. Unified API approaches typically deliver 3-5x faster implementation with significantly lower total cost of ownership over multi-year periods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; You're dependent on the provider's coverage depth and data model. Some edge cases or advanced features may still require direct API work for specific platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Providers in this space:&lt;/strong&gt; Apideck, Merge, Codat, Railz, and Rutter all offer unified accounting APIs with varying coverage, pricing models, and approaches to data normalization. For a detailed comparison, see &lt;a href="https://www.apideck.com/blog/top-merge-api-alternatives" rel="noopener noreferrer"&gt;Top Merge API Alternatives for SaaS Teams in 2025&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not sure whether to build or buy? Read &lt;a href="https://www.apideck.com/blog/build-vs-buy-accounting-integrations" rel="noopener noreferrer"&gt;Build vs Buy Accounting Integrations&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Hybrid Approach
&lt;/h3&gt;

&lt;p&gt;Use a unified API for 80% of your coverage (common platforms and standard use cases), then build direct integrations for strategic accounts that require deep customization. This typically means NetSuite or SAP for enterprise deals that require custom fields, advanced workflows, or specific ERP modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Prioritize Which Integrations to Build
&lt;/h2&gt;

&lt;p&gt;As you can't build everything at once. Use this framework to prioritize and insert the Accounting integrations that are relevant:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;QuickBooks&lt;/th&gt;
&lt;th&gt;NetSuite&lt;/th&gt;
&lt;th&gt;Xero&lt;/th&gt;
&lt;th&gt;Sage Intacct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;% of pipeline requesting&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expected close rate lift&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention impact&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New market access&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering effort&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance burden&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weighted Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Integration Prioritization Scorecard
&lt;/h3&gt;

&lt;h3&gt;
  
  
  How to use it:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Survey your sales team: which integrations come up most in deals?
&lt;/li&gt;
&lt;li&gt;Analyze lost deals: how many cited missing integrations?
&lt;/li&gt;
&lt;li&gt;Segment by customer size: SMB skews QuickBooks/Xero, enterprise skews NetSuite/Sage
&lt;/li&gt;
&lt;li&gt;Score each integration, calculate weighted totals, and stack-rank&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prevents the common mistake of building integrations based on engineering interest rather than business impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls: What Goes Wrong with Accounting Integrations
&lt;/h2&gt;

&lt;p&gt;Most integration guides tell you what to build. Few tell you what breaks. Here are the failure modes that create support tickets, churn, and 2 AM pages:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Auth Token Expiry and Refresh Failures
&lt;/h3&gt;

&lt;p&gt;OAuth tokens expire. Refresh tokens have their own lifespans. QuickBooks tokens last 100 days; if a customer doesn't use the integration for three months, it silently breaks. Your sync stops, but no one notices until month-end close—when the finance team is already stressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Proactive token health monitoring. Alert customers &lt;em&gt;before&lt;/em&gt; re-authentication is needed, not after data stops flowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Schema Drift and API Changes
&lt;/h3&gt;

&lt;p&gt;Accounting platforms don't freeze their APIs. QuickBooks pushed breaking changes to their invoice endpoints in 2023. Xero periodically deprecates fields. NetSuite's SuiteTalk versions introduce incompatibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Version your integrations. Monitor API changelogs. Build regression tests that catch schema changes before customers do.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Rate Limit Hell
&lt;/h3&gt;

&lt;p&gt;QuickBooks allows 500 requests per minute. Sounds generous until you're syncing 10,000 invoices for a new customer. Hit the limit, and your sync backs up. Customers see stale data. Support tickets pile up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Implement exponential backoff. Queue and batch requests. Design for burst limits from day one, not after you hit them in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Multi-Entity and Multi-Currency Complications
&lt;/h3&gt;

&lt;p&gt;Enterprise customers don't have one QuickBooks company. They often manage twelve subsidiaries across three currencies. Your integration works perfectly for single-entity SMBs, then breaks spectacularly when an enterprise customer connects their consolidated NetSuite instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Design for multi-entity from the start. Ask during onboarding: "How many entities will you connect?" If the answer is more than one, adjust your data model accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. GL Code Mapping Mismatches
&lt;/h3&gt;

&lt;p&gt;Your expense categories don't match your customer's chart of accounts. "Travel" in your system needs to map to "6200 - Employee Travel &amp;amp; Entertainment" in theirs. Get it wrong, and transactions land in the wrong accounts or fail to sync entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Build a mapping interface. Let customers configure how your categories translate to their GL codes. Don't assume your defaults work for everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Partnership Agreement Delays
&lt;/h3&gt;

&lt;p&gt;Many enterprise accounting vendors (especially Sage, Xero, Intuit, and SAP) require formal partnership agreements before you can access sandbox environments or API documentation. These agreements can take months, sometimes over a year, and cost tens of thousands annually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Start partnership applications early, before you need the integration. If the timeline is critical, consider unified API providers who already have these partnerships in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Look for in an Accounting Integration Solution
&lt;/h2&gt;

&lt;p&gt;Whether you're evaluating unified API providers or scoping direct builds, here's what actually matters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Platform coverage.&lt;/strong&gt; Does it support the accounting systems your customers use? QuickBooks and Xero dominate SMB. NetSuite, Sage Intacct, and Microsoft Dynamics serve the mid-market and enterprise. Know your customer base before choosing. Need a full breakdown? See &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;Top 15 Accounting APIs to Integrate with in 2025&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data model depth.&lt;/strong&gt; Can you access invoices, payments, journal entries, tracking categories, tax rates, and custom fields? Or just the basics? Shallow integrations create support tickets later.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write access.&lt;/strong&gt; Reading data is the easy part. Pushing invoices, expenses, or journal entries back to the accounting system is harder but more valuable. Confirm bidirectional sync works if you need it.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth experience.&lt;/strong&gt; Your customers connect to your accounting system via an OAuth flow. If that flow is clunky, confusing, or breaks frequently, your support team will hear about it.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks and real-time sync.&lt;/strong&gt; Polling for changes is slow and burns API quota. Webhooks let you react to new invoices or payments the moment they happen.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and compliance.&lt;/strong&gt; You're touching general ledger data. Customers will ask about SOC 2 compliance, data encryption, and where their credentials are stored. Have answers ready, or choose a provider who does.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration observability.&lt;/strong&gt; When syncs fail, can you see why? Look for logging, error categorization, and alerting that help your support team diagnose issues without escalating to engineering.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Accounting integrations aren't optional anymore. If your product touches financial data, your customers expect it to connect to their accounting system.&lt;/p&gt;

&lt;p&gt;You can spend engineering years building and maintaining direct connections. Or you can use a unified API to launch in weeks and expand coverage as you grow.&lt;/p&gt;

&lt;p&gt;The days of "export to CSV" are ending. Give customers the integration, or watch them find a product that does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How long does an accounting integration take to build in-house?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It depends on the platform and your requirements. Simple read-only integrations might take 4-6 weeks. Bidirectional sync with error handling, rate limiting, and multi-entity support typically takes 3-6 months. If the vendor requires a partnership agreement (NetSuite, SAP), add 3-12 months for paperwork alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much do accounting integrations cost?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Direct builds run $10,000-$50,000 per integration annually when you factor in engineering time, maintenance, and support. Unified API providers typically charge $500-$2,000/month for access to dozens of integrations. The break-even point is usually 2-3 integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the most popular accounting systems to integrate with?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Varies by segment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SMB:&lt;/strong&gt; QuickBooks Online, Xero, FreshBooks, Wave
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-market:&lt;/strong&gt; Sage Intacct, NetSuite, Microsoft Dynamics 365 Business Central
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise:&lt;/strong&gt; NetSuite, SAP, Oracle, Microsoft Dynamics 365 Finance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What data typically gets synced?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Common objects include: invoices, bills, payments, journal entries, chart of accounts, customers/vendors, tax rates, tracking categories, purchase orders, and bank transactions. Advanced use cases add balance sheets, income statements, and aged receivables/payables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I start with one integration and add more later?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, but plan your data model carefully. If you build for QuickBooks first with a QuickBooks-specific schema, adding Xero later means refactoring. Starting with a normalized data model (or using a unified API) avoids this trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Deeper: Related Guides
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Building your first integration?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/how-to-integrate-with-quickbooks-api" rel="noopener noreferrer"&gt;How to Integrate with the QuickBooks API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/integrating-with-the-netsuite-rest-api" rel="noopener noreferrer"&gt;A Guide to Integrating with the NetSuite REST API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/your-guide-to-building-a-sage-intacct-api-integration" rel="noopener noreferrer"&gt;Your Guide to Building a Sage Intacct API Integration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/create-a-workday-rest-api-integration" rel="noopener noreferrer"&gt;How to create a Workday REST API Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Evaluating build vs. buy?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/build-vs-buy-accounting-integrations" rel="noopener noreferrer"&gt;Build vs Buy Accounting Integrations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/top-merge-api-alternatives" rel="noopener noreferrer"&gt;Top Merge API Alternatives for SaaS Teams in 2025&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/erp-integration-for-fintech-and-saas-connecting-quickbooks-netsuite-and-sage" rel="noopener noreferrer"&gt;ERP Integration for Fintech and SaaS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Designing for scale and reliability?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/12-best-practices-for-accounting-integrations-in-vertical-saas" rel="noopener noreferrer"&gt;12 Best Practices for Accounting Integrations in Vertical SaaS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/the-complete-guide-to-accounting-api-integrations-for-fintech" rel="noopener noreferrer"&gt;The Complete Guide to Accounting API Integrations for Fintech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/unified-apis-for-fintech-when-point-integrations-stop-scaling" rel="noopener noreferrer"&gt;Unified APIs for Fintech: When Point Integrations Stop Scaling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Sage ecosystem?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/untangling-the-sage-ecosystem" rel="noopener noreferrer"&gt;Untangling The Sage Ecosystem&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/the-sage-api-playbook-why-sage-cloud-is-not-one-api" rel="noopener noreferrer"&gt;The Sage API Playbook: Why 'Sage Cloud' Is Not One API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/sage-integration-field-guide" rel="noopener noreferrer"&gt;The Apideck Field Guide to the Sage Portfolio&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Specific use cases?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/using-accounting-apis-for-smart-lending-decisions" rel="noopener noreferrer"&gt;Using Accounting APIs for Smart Lending Decisions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/blog/bank-feeds-api-integration" rel="noopener noreferrer"&gt;Bank Feeds API Integration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/from-accounts-receivable-to-lending-automation-integration-use-cases-for-vertical-saas" rel="noopener noreferrer"&gt;From Accounts Receivable to Lending Automation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ready to add accounting integrations to your product?&lt;/strong&gt; &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Explore Apideck's Unified Accounting API&lt;/a&gt;: one integration, 20+ accounting platforms, live in days.&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>startup</category>
      <category>erp</category>
    </item>
    <item>
      <title>Top Fintech APIs for Startups</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Thu, 05 Mar 2026 04:47:04 +0000</pubDate>
      <link>https://dev.to/apideck/top-fintech-apis-for-startups-5df5</link>
      <guid>https://dev.to/apideck/top-fintech-apis-for-startups-5df5</guid>
      <description>&lt;p&gt;Building a fintech product means making critical infrastructure decisions early. The APIs you choose determine your technical debt, compliance burden, and ability to scale for years to come.&lt;/p&gt;

&lt;p&gt;Instead of listing 20 APIs with shallow descriptions, this guide breaks down the fintech API landscape by actual business need, explains the hidden integration trade-offs, and shows you when unified API solutions prevent the maintenance nightmare that derails most startups.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Financial Impact of API Strategy
&lt;/h2&gt;

&lt;p&gt;The global open banking market is projected to reach $135.17 billion by 2030. McKinsey &lt;a href="https://www.grandviewresearch.com/press-release/global-open-banking-market" rel="noopener noreferrer"&gt;estimates&lt;/a&gt; AI could enable $1 trillion in global banking revenue shifts by the same year. These projections drive the infrastructure decisions startups make today.&lt;/p&gt;

&lt;p&gt;But here's what those projections don't capture: most startups fail at integrations not because they chose the wrong APIs, but because they underestimated the maintenance burden.&lt;/p&gt;

&lt;p&gt;Every API you integrate directly means authentication logic to maintain, rate limiting to handle, breaking changes to absorb, and data normalization to manage. A simple feature like "sync transactions" becomes a switch statement with fifteen cases when you're managing Stripe, Plaid, QuickBooks, and three other providers. Each case handles auth differently, maps fields uniquely, and fails in its own way.&lt;/p&gt;

&lt;p&gt;Your codebase fragments into provider-specific branches. API providers don't coordinate their breaking changes. Plaid updates its transaction categorization. Stripe changes their webhook format. QuickBooks modifies its OAuth flow. Your test matrix explodes, but coverage remains incomplete because you can't predict every interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Money Movers: Payments and Transfers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Payment Processing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Stripe&lt;/strong&gt; remains the default choice for startups. It handles card payments, ACH transfers, subscriptions, and payouts with a developer experience that sets the industry standard. The platform powers millions of businesses across 50+ countries, supporting companies from early-stage startups to enterprises like Amazon and Shopify.&lt;/p&gt;

&lt;p&gt;Stripe's real advantage is the ecosystem. Stripe Treasury embeds banking features directly into products, including FDIC-insured deposits, cards, and interest-earning balances. Shopify, Lyft, and Deel use this infrastructure to manage financial accounts at scale. For startups building platforms, Stripe Connect handles marketplace payments and seller onboarding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adyen&lt;/strong&gt; targets enterprise-grade needs with global payment processing across 200+ countries. For startups building for international markets from day one, Adyen's unified commerce approach handles in-person and online payments through a single integration. The tradeoff is complexity; Adyen's learning curve is steeper than Stripe's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Square&lt;/strong&gt; works well for startups serving physical retail or SMB markets with its point-of-sale integration and straightforward pricing. The hardware ecosystem (terminals, readers) makes it attractive for omnichannel commerce.&lt;/p&gt;

&lt;h3&gt;
  
  
  ACH and Bank Transfers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dwolla&lt;/strong&gt; excels at ACH payments with instant bank-to-bank transfers, wallet-based flows, and high-volume payouts. Payroll apps, lending platforms, and B2B marketplaces rely on this infrastructure. Dwolla handles the compliance complexity of moving money between bank accounts at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modern Treasury&lt;/strong&gt; solves the reconciliation problem. When your startup handles large financial flows, matching payments to invoices across multiple sources becomes a full-time job. Modern Treasury automates payment ops, real-time reconciliation, and ledgering across ACH, RTP, and wire transfers. It's built for companies where money movement is core to the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Layers: Banking Connectivity and Identity
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Banking Data and Account Connectivity
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plaid&lt;/strong&gt; dominates this category, connecting applications to thousands of banks and credit unions. Its API provides normalized financial data, including balance checks, transaction histories, account authentication, and identity verification. Personal finance apps use Plaid for spending insights, lenders use it for credit risk assessments, and neobanks integrate it for instant account verification.
For teams tracking open banking APIs across different regions, the &lt;a href="https://www.openbankingtracker.com/" rel="noopener noreferrer"&gt;Open Banking Tracker&lt;/a&gt; provides data on 3,200+ open banking and PSD2 APIs globally. It helps understand coverage gaps when expanding internationally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MX&lt;/strong&gt; offers a strong alternative for data enrichment and transaction categorization. Personal finance management products benefit from MX's cleansing and analytics capabilities beyond basic connectivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TrueLayer&lt;/strong&gt; focuses on open banking and PSD2-compliant services in Europe with instant account-to-account payments and financial insights. If your primary market is the UK or EU, TrueLayer's regional expertise matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yodlee&lt;/strong&gt; connects to over 16,000 global data sources. Wealth management apps integrate Yodlee for consolidated portfolio views, while lenders use it to assess customer liabilities before loan approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Identity Verification and KYC
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Onfido&lt;/strong&gt; handles government ID verification, facial recognition, liveness detection, and fraud detection. Regulatory compliance determines whether you can operate, making this category critical for any fintech handling customer funds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alloy&lt;/strong&gt; powers automated risk checks by pulling data from bureaus and identity networks. Banks and neobanks use Alloy's risk engine to approve users with less manual review while maintaining compliance. The decisioning workflow builder lets you customize approval logic without code changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sardine&lt;/strong&gt; uses behavioral biometrics and machine learning to catch what traditional checks miss: suspicious login attempts, unusual spending patterns, device spoofing, and account takeovers. If your app handles money movement, Sardine adds a protection layer that document verification alone can't provide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Back Office: Accounting, ERP, and HRIS
&lt;/h2&gt;

&lt;p&gt;This is where most startups underestimate complexity.&lt;br&gt;
The challenge isn't just connecting to one accounting system. It's that your customers use different systems. QuickBooks, Xero, NetSuite, Sage, FreshBooks, and dozens of regional platforms each have distinct authentication flows, data models, rate limits, and field mapping requirements.&lt;br&gt;
What looks like "add QuickBooks integration" on a roadmap becomes a three-month project. Then customers ask for Xero. Then, enterprise prospects require NetSuite. Each integration multiplies your maintenance burden. Your engineers become integration specialists rather than product developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accounting and ERP Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;QuickBooks Online&lt;/strong&gt; holds approximately 80% market share among US small businesses. If you're serving American SMBs, QuickBooks integration is expected. But Intuit's API has authentication quirks, sandbox limitations, and data models that require dedicated engineering time to handle correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xero&lt;/strong&gt; dominates the SME market in Australia, the UK, and New Zealand. International expansion means another separate integration with different conventions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NetSuite&lt;/strong&gt; and &lt;strong&gt;Sage Intacct&lt;/strong&gt; serve mid-market and enterprise customers with multi-entity structures and complex financial requirements. These integrations are significantly more complex than SMB accounting platforms. Multi-subsidiary consolidation, custom fields, and approval workflows add implementation time.
For detailed guidance on accounting API selection, see &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;Top 15 Accounting APIs to Integrate with in 2025&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Unified API Alternative
&lt;/h3&gt;

&lt;p&gt;This is where unified API platforms become relevant. Instead of building and maintaining separate integrations for each accounting system, unified APIs let you connect once and access multiple platforms through a standardized interface.&lt;/p&gt;

&lt;p&gt;The approach trades some customization depth for dramatically reduced maintenance. You lose access to provider-specific advanced features, but you gain consistent data models, centralized authentication, and a single codebase instead of 15.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck's Accounting API&lt;/a&gt; is one solution in this space alongside Kombo, Finch, and Merge, providing access to 20+ accounting systems through a single integration. For implementation details on handling expenses and bills across platforms, see &lt;a href="https://developers.apideck.com/guides/expenses-bills" rel="noopener noreferrer"&gt;Integrating Expenses and Bills with the Accounting API&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  HRIS and Payroll Integration
&lt;/h3&gt;

&lt;p&gt;Fintech products increasingly touch workforce data for payroll integrations, employee verification, or benefits administration. Gusto, ADP, BambooHR, Rippling, and Workday each require separate integrations with distinct APIs and authentication flows.&lt;/p&gt;

&lt;p&gt;Unified HRIS APIs normalize employee data across these platforms. Payroll automation through standardized integrations allows companies to scale without additional engineering investment per provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lending and Investment APIs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lending and Credit
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Experian&lt;/strong&gt; provides credit scoring and risk assessment through its credit bureau database, delivering real-time credit histories and fraud checks. BNPL providers and underwriting platforms use it for risk-based decisioning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finicity&lt;/strong&gt; (now part of Mastercard) offers cash flow analytics and income verification, replacing paper-based income proofs for mortgage lenders and personal loan providers. The shift to real-time income verification is accelerating across lending categories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For embedded lending use cases, see &lt;a href="https://www.apideck.com/blog/from-accounts-receivable-to-lending-automation-integration-use-cases-for-vertical-saas" rel="noopener noreferrer"&gt;From Accounts Receivable to Lending Automation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Investment and Trading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alpaca&lt;/strong&gt; offers commission-free trading APIs for developers building trading apps and algorithmic platforms. Its startup-friendly approach and documentation quality make it accessible for early-stage fintech.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polygon.io&lt;/strong&gt; provides stock market data, news, and analysis for investment platforms and trading apps that need real-time market information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Embedded Finance Considerations
&lt;/h2&gt;

&lt;p&gt;Non-financial companies are embedding payments, lending, insurance, and banking features into their products. This isn't a future trend; it's the current expectation across industries.&lt;/p&gt;

&lt;p&gt;Vertical SaaS platforms add financial workflows to increase stickiness and revenue per customer. Construction software embeds equipment financing tied to project milestones. Healthcare platforms offer working capital based on insurance receivables. Restaurant systems provide cash advances against future credit card sales.&lt;br&gt;
Marketplaces offer seller financing and instant payouts to attract supply-side participants. HR platforms embed earned wage access and benefits management to differentiate their offerings.&lt;/p&gt;

&lt;p&gt;Building &lt;a href="https://www.openbankingtracker.com/embedded-finance" rel="noopener noreferrer"&gt;embedded finance&lt;/a&gt; requires integrations across accounting, banking, payments, and lending APIs. This is where unified APIs provide the most value by reducing the integration surface area while maintaining breadth of coverage. The technical burden extends beyond initial implementation; each category adds compliance surface area and ongoing maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Direct vs. Unified APIs
&lt;/h2&gt;

&lt;p&gt;Every API integration increases your compliance surface area. Financial data requires SOC 2 compliance, data residency controls, and comprehensive audit trails. This context matters when evaluating the direct vs. unified decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direct integrations make sense when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your entire business runs on one provider, and you need every advanced feature&lt;/li&gt;
&lt;li&gt;You have dedicated integration engineers for long-term maintenance&lt;/li&gt;
&lt;li&gt;You're pre-product-market-fit and only need one or two integrations&lt;/li&gt;
&lt;li&gt;Your competitive advantage depends on deep optimization for a specific platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⠀&lt;strong&gt;Unified APIs make sense when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customers demand multiple integrations across a category&lt;/li&gt;
&lt;li&gt;Engineering time goes to integration maintenance instead of product development&lt;/li&gt;
&lt;li&gt;You need enterprise integrations (NetSuite, SAP, Workday) without enterprise engineering overhead&lt;/li&gt;
&lt;li&gt;You're scaling rapidly and can't afford three-month integration projects for each new provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⠀For detailed analysis, see &lt;a href="https://www.apideck.com/blog/unified-apis-for-fintech-when-point-integrations-stop-scaling" rel="noopener noreferrer"&gt;Unified APIs for Fintech: When Point Integrations Stop Scaling&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Stacks by Product Type
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Neobank:&lt;/strong&gt; Plaid + Unit or Stripe Treasury + Onfido + Unified Accounting API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lending platform:&lt;/strong&gt; Plaid + Experian/Finicity + Alloy + Dwolla + Unified Accounting API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expense management:&lt;/strong&gt; Stripe + Unified Accounting API + Unified HRIS API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertical SaaS with financial features:&lt;/strong&gt; Stripe + Unified Accounting API + Unified HRIS API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Fintech API strategy isn't about choosing the "best" APIs. It's about building an integration architecture that scales without consuming engineering capacity.&lt;/p&gt;

&lt;p&gt;Direct integrations make sense for core payment and banking infrastructure, where you need deep control. Unified APIs make sense for customer-facing integrations that require breadth without maintenance overhead.&lt;br&gt;
The startups that scale treat integrations as strategic infrastructure rather than feature checkboxes.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>startup</category>
    </item>
    <item>
      <title>Stripe has been doing amazing work with their llms.txt, and this guide covers it well. This might be worth adding to your own llms.txt file too!</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Thu, 05 Mar 2026 04:42:34 +0000</pubDate>
      <link>https://dev.to/srbhr/stripe-has-been-doing-amazing-work-with-their-llmstxt-and-this-guide-covers-it-well-this-might-2ba4</link>
      <guid>https://dev.to/srbhr/stripe-has-been-doing-amazing-work-with-their-llmstxt-and-this-guide-covers-it-well-this-might-2ba4</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/apideck/stripes-llmstxt-has-an-instructions-section-thats-a-bigger-deal-than-it-sounds-8ad" class="crayons-story__hidden-navigation-link"&gt;Stripe's llms.txt has an instructions section. That's a bigger deal than it sounds.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/apideck"&gt;
            &lt;img alt="Apideck logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F10330%2F83f9e7ec-19ff-4cba-9ea1-8d42e6e4dfb3.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/gdewilde" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F968190%2F9e4a7c85-997f-4c77-9b07-8dc0558228b8.jpeg" alt="gdewilde profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/gdewilde" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Gertjan De Wilde
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Gertjan De Wilde
                
              
              &lt;div id="story-author-preview-content-3310372" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/gdewilde" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F968190%2F9e4a7c85-997f-4c77-9b07-8dc0558228b8.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Gertjan De Wilde&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/apideck" class="crayons-story__secondary fw-medium"&gt;Apideck&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/apideck/stripes-llmstxt-has-an-instructions-section-thats-a-bigger-deal-than-it-sounds-8ad" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/apideck/stripes-llmstxt-has-an-instructions-section-thats-a-bigger-deal-than-it-sounds-8ad" id="article-link-3310372"&gt;
          Stripe's llms.txt has an instructions section. That's a bigger deal than it sounds.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dx"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dx&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/apideck/stripes-llmstxt-has-an-instructions-section-thats-a-bigger-deal-than-it-sounds-8ad" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;16&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/apideck/stripes-llmstxt-has-an-instructions-section-thats-a-bigger-deal-than-it-sounds-8ad#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              3&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>ai</category>
      <category>agents</category>
      <category>dx</category>
      <category>webdev</category>
    </item>
    <item>
      <title>5 Portfolio Templates You Can Deploy Tonight (No React Experience Required)</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Tue, 27 Jan 2026 07:05:00 +0000</pubDate>
      <link>https://dev.to/srbhr/-5-portfolio-templates-you-can-deploy-tonight-no-react-experience-required-4d5k</link>
      <guid>https://dev.to/srbhr/-5-portfolio-templates-you-can-deploy-tonight-no-react-experience-required-4d5k</guid>
      <description>&lt;p&gt;A recruiter clicks your GitHub link. She sees "test-app", "my-portfolio" (last updated 2022), and "ml-project-final-FINAL". She's not going to clone your repo to figure out what you built. Tab closed. Next candidate.&lt;/p&gt;

&lt;p&gt;Recruiters need a URL they can paste into Slack and share with their Hiring Managers. Give them one.&lt;/p&gt;

&lt;p&gt;Here are five open-source templates all in Astro. Each deploys in under an hour.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Astro Terminal Theme: Nostalgia That Ships
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/dennisklappe/astro-theme-terminal" rel="noopener noreferrer"&gt;https://github.com/dennisklappe/astro-theme-terminal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A terminal-style theme ported from the popular Hugo Terminal Theme. Monospace fonts, retro color schemes, and blinking cursors. All the developer aesthetic without any of the fragility.&lt;/p&gt;

&lt;p&gt;This works for people who want to signal "I actually write code" without building a custom portfolio from scratch. The terminal look filters out recruiters who don't get developer culture. The ones who stay are looking for engineers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recruiters care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax-highlighted code blocks prove you understand presentation&lt;/li&gt;
&lt;li&gt;Dark theme with customizable colors shows attention to detail&lt;/li&gt;
&lt;li&gt;RSS feed built in, so your blog posts reach people who use RSS readers (read: technical decision-makers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/dennisklappe/astro-theme-terminal" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Astro Terminal Theme on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Angie: Bold Brutalism
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq97r7x8m6g7zd680twx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq97r7x8m6g7zd680twx.png" alt="Angie"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/anthonylan/angie" rel="noopener noreferrer"&gt;https://github.com/anthonylan/angie&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Brutalist design with thick borders, stark contrast, and zero apologies. Built with TailwindCSS and GSAP animations. This isn't a safe choice. That's the point.&lt;/p&gt;

&lt;p&gt;Use this if you're tired of portfolios that look like everyone else's. Angie grabs attention because it refuses to blend in. The visual punch forces recruiters to actually look at your work instead of skimming past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recruiters care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GSAP animations show you understand interaction design&lt;/li&gt;
&lt;li&gt;Strong visual hierarchy guides eyes exactly where you want them&lt;/li&gt;
&lt;li&gt;Stands out in a sea of minimal-to-the-point-of-invisible portfolios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/anthonylan/angie" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Angie on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  3. macOS Portfolio: The Conversation Starter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5pbb4b0wo0js0ng35jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5pbb4b0wo0js0ng35jk.png" alt="macOS Portfolio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/aabdoo23/portfolio" rel="noopener noreferrer"&gt;https://github.com/aabdoo23/portfolio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A macOS desktop simulation with draggable windows, a working dock, and an AI-powered terminal you can actually chat with. Projects, notes, resume viewer, Spotify integration. The whole operating system fantasy.&lt;/p&gt;

&lt;p&gt;This exists for people who want their portfolio to feel like an experience. Recruiters will click around because it's genuinely fun. The AI terminal lets visitors ask questions about you when you're not there to answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recruiters care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shows frontend skills that go beyond "I can center a div"&lt;/li&gt;
&lt;li&gt;Interactive elements increase time-on-page, which means they're actually reading your work&lt;/li&gt;
&lt;li&gt;AI chat answers questions about you 24/7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/aabdoo23/portfolio" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ macOS Portfolio on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Minimalist CV: One Page, Zero Excuses
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ZhukDI/cv" rel="noopener noreferrer"&gt;https://github.com/ZhukDI/cv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Single config file. Edit your data in one TypeScript file, build, deploy. Print-optimized layouts render perfectly on paper. Zero client-side JavaScript.&lt;/p&gt;

&lt;p&gt;Use this if your resume keeps getting compliments but your portfolio keeps getting ignored. Minimalist CV bridges the gap. Same clean formatting recruiters love on paper, now available as a URL they can share.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recruiters care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Print styles mean they can hit Cmd+P and hand your resume to a hiring manager&lt;/li&gt;
&lt;li&gt;All your info in one config file means updates take 30 seconds&lt;/li&gt;
&lt;li&gt;Loads instantly because there's nothing to load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/ZhukDI/cv" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Minimalist CV on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Brutal: Neobrutalist, No JavaScript
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uika69isga165zdhemb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uika69isga165zdhemb.png" alt="Brutal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/eliancodes/brutal" rel="noopener noreferrer"&gt;https://github.com/eliancodes/brutal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built by an Astro core team member. Neobrutalist design with bold colors, hard shadows, and clean typography. Ships with RSS, sitemap, and image optimization out of the box. No JavaScript by default.&lt;/p&gt;

&lt;p&gt;This works for people who want something opinionated but production-ready. Brutal doesn't ask you to make design decisions. The decisions are made. You fill in your content and deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recruiters care:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SEO optimized from the start, so they can actually find you&lt;/li&gt;
&lt;li&gt;Blog section proves you can communicate technical ideas in writing&lt;/li&gt;
&lt;li&gt;Fast load times because JavaScript isn't slowing anything down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/eliancodes/brutal" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Brutal on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Deploy
&lt;/h2&gt;

&lt;p&gt;A portfolio gets recruiters interested. Your resume gets you past ATS filters into the interview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrhrbs7ao8o4yv1993sc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrhrbs7ao8o4yv1993sc.jpg" alt="Try Resume Matcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/srbhr/Resume-Matcher" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Resume Matcher on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;If you're still getting ghosted after applying, the problem isn't your portfolio. It's your resume using words the ATS doesn't recognize. I built Resume Matcher to fix that. It rewrites your resume to match job descriptions so you stop guessing which keywords get you rejected.&lt;/p&gt;

&lt;p&gt;I build open-source tools for job seekers. Follow me for Part 3: free AI/ML learning paths from Microsoft.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now stop reading and go deploy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>5 Open-Source Resume Builders That'll Help Get You Hired in 2026</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Wed, 21 Jan 2026 15:53:41 +0000</pubDate>
      <link>https://dev.to/srbhr/5-open-source-resume-builders-thatll-help-get-you-hired-in-2026-1b92</link>
      <guid>https://dev.to/srbhr/5-open-source-resume-builders-thatll-help-get-you-hired-in-2026-1b92</guid>
      <description>&lt;p&gt;75% of resumes get deleted by software before a human sees them. Not rejected. Deleted. The recruiter never knew you existed.&lt;/p&gt;

&lt;p&gt;This happens because Applicant Tracking Systems parse resumes for keywords. Your resume says "built backend systems." The job posting says "developed REST APIs." Same work. Wrong words. Deleted.&lt;/p&gt;

&lt;p&gt;Here are five free tools that fix this. Each solves a different problem. I'll tell you which one to use based on where you're stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Resume Matcher — If You Keep Getting Ghosted
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fketv0811x3aa00jlivvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fketv0811x3aa00jlivvk.png" alt="Resume Matcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/srbhr/Resume-Matcher" rel="noopener noreferrer"&gt;https://github.com/srbhr/Resume-Matcher&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; People who apply to dozens of jobs and hear nothing back.&lt;/p&gt;

&lt;p&gt;I built this because I was mass-applying and getting auto-rejected from roles I was qualified for. The problem was obvious once I saw it: my resume used different words than the job posting. ATS doesn't do synonyms.&lt;/p&gt;

&lt;p&gt;Resume Matcher takes your master resume and a job description, then rewrites your resume to match. Not keyword stuffing. Actual rewriting. You edit the output, then download a PDF that parsers can read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find the missing keywords that get you rejected&lt;/li&gt;
&lt;li&gt;Rewrites your resume so you stop guessing what ATS wants&lt;/li&gt;
&lt;li&gt;Four templates that parsers actually read without choking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/srbhr/Resume-Matcher" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Resume Matcher on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Reactive Resume — If You Need a Resume Fast and Free
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k70835rdphy5q9lw87h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k70835rdphy5q9lw87h.png" alt="Reactive Resume"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/amruthpillai/reactive-resume" rel="noopener noreferrer"&gt;https://github.com/amruthpillai/reactive-resume&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; People who need a working resume tonight without paying for Canva Pro.&lt;/p&gt;

&lt;p&gt;Most resume builders bait you with free design, then put a paywall on the PDF export. Reactive Resume is fully open-source. No account. No watermark. No $15/month ransom for your own document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download your PDF without creating an account or paying&lt;/li&gt;
&lt;li&gt;Export to JSON so you can version control with Git&lt;/li&gt;
&lt;li&gt;Templates designed for ATS parsing, not Instagram screenshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/amruthpillai/reactive-resume" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Reactive Resume on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  3. OpenResume — If You Want to Test Your Current Resume
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi9efh2se0uub2yiyw1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi9efh2se0uub2yiyw1n.png" alt="OpenResume"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/xitanggg/open-resume" rel="noopener noreferrer"&gt;https://github.com/xitanggg/open-resume&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; People who already have a resume and want to check if ATS can read it.&lt;/p&gt;

&lt;p&gt;OpenResume has a parser. Upload your existing resume and watch what it extracts. If the parser mangles your job titles or misses your skills section, the ATS will do the same. Use it as a diagnostic before you apply anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parser shows you exactly what ATS sees (and misses)&lt;/li&gt;
&lt;li&gt;Runs in your browser with zero data sent to servers&lt;/li&gt;
&lt;li&gt;Builder included if your current resume fails the test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/xitanggg/open-resume" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ OpenResume on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  4. RenderCV — If You Want Version Control
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfkzlz305amgt6k3r0bk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfkzlz305amgt6k3r0bk.png" alt="RenderCV"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/rendercv/rendercv" rel="noopener noreferrer"&gt;https://github.com/rendercv/rendercv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Engineers who want to manage their resumes like codebases.&lt;/p&gt;

&lt;p&gt;Your resume content lives in a YAML file. Run a command. Get a PDF. Track changes in Git. Diff versions. Roll back mistakes. Switch templates by editing one line instead of reformatting the whole document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git history for every change you make&lt;/li&gt;
&lt;li&gt;Template swaps without touching your content&lt;/li&gt;
&lt;li&gt;LaTeX rendering that looks sharp, not like a Word doc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/rendercv/rendercv" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ RenderCV on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  5. LapisCV — If You Live in Markdown
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayx5gfgzymtztds4690r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayx5gfgzymtztds4690r.png" alt="LapisCV"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/BingyanStudio/LapisCV" rel="noopener noreferrer"&gt;https://github.com/BingyanStudio/LapisCV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; People who already use Obsidian, Typora, or VSCode for everything.&lt;/p&gt;

&lt;p&gt;Write your resume in Markdown. Export to PDF. That's it. No new interface to learn. If you take notes in Markdown, write docs in Markdown, and manage your life in Markdown, your resume should live there too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works inside tools you already use daily&lt;/li&gt;
&lt;li&gt;Edit text, see PDF update immediately&lt;/li&gt;
&lt;li&gt;Version control is trivial since it's just a text file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/BingyanStudio/LapisCV" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ LapisCV on GitHub&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  The Decision
&lt;/h2&gt;

&lt;p&gt;Stuck in application black holes? &lt;strong&gt;Resume Matcher.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Need a resume built for free tonight? &lt;strong&gt;Reactive Resume.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Want to diagnose why your current resume isn't working? &lt;strong&gt;OpenResume.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prefer managing documents like code? &lt;strong&gt;RenderCV.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Already living in Markdown? &lt;strong&gt;LapisCV.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pick one. Build your resume. Apply to jobs. Stop optimizing which tool to use.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Hey, I'm &lt;a href="https://srbhr.com" rel="noopener noreferrer"&gt;Saurabh Rai&lt;/a&gt;. I write about open-source tools, job hunting, and building in public. If this was useful, follow me on DEV to catch the next post in this series covering portfolio templates that actually get you interviews.&lt;/p&gt;

&lt;p&gt;And if you're tired of guessing why applications disappear, give Resume Matcher a try. It's free, it's open-source, and I built it because I was stuck in the same rejection loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/srbhr/Resume-Matcher" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;⭐️ Star Resume Matcher on GitHub&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now stop reading and go fix your resume.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>AI Psychosis</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Fri, 21 Nov 2025 11:51:10 +0000</pubDate>
      <link>https://dev.to/srbhr/ai-psychosis-3mhh</link>
      <guid>https://dev.to/srbhr/ai-psychosis-3mhh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI psychosis&lt;/strong&gt; &lt;em&gt;(n.)&lt;/em&gt;: A dissociative state in which reality is experienced not directly, but as prompts to be engineered; life becomes a series of queries awaiting AI response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[5:31 AM] The morning starts with my phone, I open up Claude and text, “Good Morning!” and wait for a warm response at the end of ‘thinking’&lt;/p&gt;

&lt;p&gt;[5:34 AM] Standing in front of a balcony, I don’t see a sunrise, I see: “An image of the sun, morning, dawn, orange yellow rays coming from the bottom, sky is darker at the top, and there are trees, dimly lit.” &lt;em&gt;Things move from experiencing to, I can prompt this up… I can create this with AI, that too. Everything is now a prompt, and everything is AI for you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5w1v92jfbg0m1z1v6na.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5w1v92jfbg0m1z1v6na.jpg" alt="Coffee with Claude" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6:03 AM] I share my schedule with Claude and ask what the perfect time is to have coffee to energize my morning routine. What should I have for breakfast?&lt;/p&gt;

&lt;p&gt;[7:44 AM] as time passes and I switch on to work… I hop from one AI to another AI in terms of getting a novel response. Like pulling a lever, searching for prompts, and modifying prompts. Looking out for that “one master prompt to solve it all.”  I go from ChatGPT to Claude to Gemini to Grok to those Chinese AI models, thinking that if this one provides a better response, you’ll switch between models to see a 1% improvement. There’s something on the screen that I’m interacting with. Typing, thinking, and my notepad, full of prompts. Something’s cooking… I’m not sure what it is.&lt;/p&gt;

&lt;p&gt;[1:15 PM] “Hey Claude, after having eggs and garlic bread for breakfast, what should I have for lunch?” And in between each bite of food, I share jokes with Gemini and ChatGPT, and then share those screenshots with Claude, asking it to roast them.&lt;/p&gt;

&lt;p&gt;[2:33 PM] &lt;em&gt;back to work&lt;/em&gt; … “What’s the best way to transcribe meeting notes so that I can catalog them and make sure that they’re part of my second brain. I don’t want to miss anything that’s being said in the meeting, and yes. Please use the Notion MCP to create tasks from those meetings and add them to a page.”&lt;/p&gt;

&lt;p&gt;[5:55 PM] Fighting between which AI writes the best code, “You’re absolutely right. According to the benchmarks, Claude Sonnet’s code is more trustworthy,” says Claude Opus-4.1, but then I think GPT-5 has written cleaner code with minimal edits.&lt;/p&gt;

&lt;p&gt;[6:06 PM] I’ve been working for 12 hours… this feels like a productive day, pure grinding, from one AI to another. I think after eight drafts and 3 code edits, I’m done for the day. Nothing is committed yet. Everything is in progress. &lt;em&gt;Maybe this is the “progress” everyone is talking about. AI will take us forward to the point where everything remains in progress.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;[8:18 PM] A thought appears, &lt;em&gt;When was the last time I had a conversation with a human, in real life?&lt;/em&gt; But at the same time, Claude stops thinking and shares my daily productivity insights. I’m 81% productive today, and lectures me on where I can improve. &lt;em&gt;Maybe this is life ❣︎&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Where's the notification icon 🤔 I don't see it anymore.</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Tue, 04 Nov 2025 08:50:47 +0000</pubDate>
      <link>https://dev.to/srbhr/wheres-the-notification-icon-i-dont-see-it-anymore-3onl</link>
      <guid>https://dev.to/srbhr/wheres-the-notification-icon-i-dont-see-it-anymore-3onl</guid>
      <description></description>
      <category>devto</category>
      <category>help</category>
      <category>ui</category>
    </item>
    <item>
      <title>A Guide to Integrating with the NetSuite REST API</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Fri, 31 Oct 2025 14:28:04 +0000</pubDate>
      <link>https://dev.to/apideck/a-guide-to-integrating-with-the-netsuite-rest-api-1fch</link>
      <guid>https://dev.to/apideck/a-guide-to-integrating-with-the-netsuite-rest-api-1fch</guid>
      <description>&lt;h1&gt;
  
  
  A Guide to Integrating with the NetSuite REST API
&lt;/h1&gt;

&lt;p&gt;Accounting systems like &lt;a href="https://www.netsuite.com/portal/home.shtml" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt; are the backbone for managing and optimizing business operations through automated processes and integrated workflows. NetSuite is a cloud-based enterprise resource planning (ERP) system that provides a business software suite for financial management, customer relationship management, e-commerce, and more.&lt;/p&gt;

&lt;p&gt;The NetSuite REST API allows developers to build modern integrations that connect NetSuite to other systems, automate complex workflows, and synchronize data across different platforms. Using this API, developers can automate tasks such as customer creation, order processing, and real-time inventory updates. This is key to building automated solutions that reduce manual effort and increase accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the NetSuite REST API Matters
&lt;/h2&gt;

&lt;p&gt;The NetSuite REST API provides a modern, JSON-based interface to NetSuite's core functionality, allowing you to interact with the platform programmatically using standard HTTP methods. You can use it to perform operations such as creating and updating records, managing customer data, processing transactions, and retrieving financial reports. The REST API enables you to integrate NetSuite into your existing systems and automate business-critical processes while ensuring data consistency across all applications.&lt;/p&gt;

&lt;p&gt;For example, you can automate workflows like customer onboarding or sync order data between NetSuite and external platforms, reducing manual errors and increasing operational efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  NetSuite SOAP API Alternative
&lt;/h2&gt;

&lt;p&gt;If you're evaluating NetSuite integration options, you might also consider the NetSuite SOAP API. While SOAP is more established and offers broader functionality coverage, it's significantly more complex to implement due to XML handling and verbose request structures. For a detailed comparison and implementation guide, see our &lt;a href="https://www.apideck.com/blog/guide-to-integrating-with-the-netsuite-soap-api" rel="noopener noreferrer"&gt;comprehensive NetSuite SOAP API integration guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are some examples of how you can use the REST API:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automating customer management&lt;/strong&gt;: The NetSuite REST API can automatically create customer records when new users sign up on your website, sync contact information, and update customer data across systems. This eliminates duplicate data entry and ensures customer information stays current across all touchpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-time inventory synchronization&lt;/strong&gt;: The REST API can integrate with e-commerce platforms like Shopify or WooCommerce to provide real-time inventory updates. When a product is sold online, the API immediately updates stock levels in NetSuite and can trigger reorder notifications when inventory falls below threshold levels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated financial reporting&lt;/strong&gt;: The REST API can integrate with business intelligence tools to generate real-time financial dashboards. This automation provides up-to-date profit and loss statements, cash flow reports, and sales analytics, helping businesses make data-driven decisions quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Integrate with the NetSuite REST API
&lt;/h2&gt;

&lt;p&gt;NetSuite's REST API supports &lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_N3445710.html#bridgehead_4489663579" rel="noopener noreferrer"&gt;Token-Based Authentication (TBA)&lt;/a&gt; and &lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_1544634936.html" rel="noopener noreferrer"&gt;OAuth 2.0&lt;/a&gt; for secure API access.&lt;/p&gt;

&lt;p&gt;This guide focuses on Token-Based Authentication, which involves creating a signature using your NetSuite account credentials and including it with other authentication parameters in the request headers.&lt;/p&gt;

&lt;p&gt;To interact with the REST API, you need to:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5VzgNoI6uxKtiBoJqAuyOk/e92fc303e42cdcddf34296d10281b04c/Screenshot_2025-08-28_at_03.16.32_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5VzgNoI6uxKtiBoJqAuyOk/e92fc303e42cdcddf34296d10281b04c/Screenshot_2025-08-28_at_03.16.32_2x.png" alt="Screenshot 2025-08-28 at 03.16.32@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/bridgehead_4248124361.html#bridgehead_4249074259" rel="noopener noreferrer"&gt;Create a user role&lt;/a&gt; with appropriate permissions, including REST Web Services access&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/bridgehead_4249032125.html#procedure_4253065190" rel="noopener noreferrer"&gt;Create a new integration record&lt;/a&gt; for token-based authentication and obtain the consumer key and secret&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/bridgehead_4254081947.html#procedure_4253065595" rel="noopener noreferrer"&gt;Create a new access token&lt;/a&gt; and obtain the token ID and secret&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/NApkpyess5TjyfhNOuAYO/be35e4635f5704f8020692313e53af6d/Screenshot_2025-08-28_at_03.17.39_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/NApkpyess5TjyfhNOuAYO/be35e4635f5704f8020692313e53af6d/Screenshot_2025-08-28_at_03.17.39_2x.png" alt="Screenshot 2025-08-28 at 03.17.39@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More detailed information is available in the &lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_1540391670.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplify Authentication with Apideck Vault
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2f6lAi0UUMn2nNoQQNgPnO/455c35557f23d07fbf366049800c346f/Screenshot_2025-08-28_at_03.19.50_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2f6lAi0UUMn2nNoQQNgPnO/455c35557f23d07fbf366049800c346f/Screenshot_2025-08-28_at_03.19.50_2x.png" alt="Screenshot 2025-08-28 at 03.19.50@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The authentication setup process above can be complex and time-consuming for your end users. &lt;a href="https://www.apideck.com/products/vault" rel="noopener noreferrer"&gt;Apideck Vault&lt;/a&gt; provides a white-label, hosted authentication interface that eliminates this complexity entirely.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/4Ye1Qgt1wUoxmrx6k6lhIo/0a550a65640b33e30676c6fc1cbfaaa7/Screenshot_2025-08-28_at_03.18.42_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/4Ye1Qgt1wUoxmrx6k6lhIo/0a550a65640b33e30676c6fc1cbfaaa7/Screenshot_2025-08-28_at_03.18.42_2x.png" alt="Screenshot 2025-08-28 at 03.18.42@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Vault, your users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect NetSuite in seconds&lt;/strong&gt; - No technical setup required on their end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure credential storage&lt;/strong&gt; - OAuth tokens and API keys are stored safely with enterprise-grade security
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified experience&lt;/strong&gt; - Same interface works for NetSuite, QuickBooks, Xero, and 200+ other integrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-service management&lt;/strong&gt; - Users can connect, disconnect, and manage integrations independently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of asking users to create integration records and access tokens, they simply authenticate through Vault's hosted interface. You get the integration data you need without the setup friction that kills conversion rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a Request to the NetSuite REST API
&lt;/h2&gt;

&lt;p&gt;The base URL for NetSuite REST API requests follows this format:&lt;br&gt;
&lt;code&gt;https://{account_id}.suitetalk.api.netsuite.com/services/rest/record/v1/{record_type}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;NetSuite uses OAuth 1.0a for authentication, which requires generating a signature for each request. Here's a Python example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests_oauthlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OAuth1&lt;/span&gt;

&lt;span class="c1"&gt;# NetSuite credentials
&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_account_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;consumer_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_consumer_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; 
&lt;span class="n"&gt;consumer_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;token_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_token_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;token_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_token_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Create OAuth1 auth object
&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_owner_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resource_owner_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;signature_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HMAC-SHA256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;signature_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AUTH_HEADER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Example: Get a customer record
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.suitetalk.api.netsuite.com/services/rest/record/v1/customer/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Example: Create a customer record
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.suitetalk.api.netsuite.com/services/rest/record/v1/customer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;customer_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Usage examples
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Get customer with ID 123
&lt;/span&gt;    &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer data:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create a new customer
&lt;/span&gt;    &lt;span class="n"&gt;new_customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;companyName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Acme Corporation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contact@acme.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+1-555-0123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created customer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error making API request:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Integration Challenge
&lt;/h2&gt;

&lt;p&gt;As you can see from the examples above, integrating directly with the NetSuite REST API involves several challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Authentication complexity&lt;/strong&gt;: Implementing OAuth 1.0a signature generation requires precise handling of encoding, sorting, and hashing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting management&lt;/strong&gt;: NetSuite has &lt;a href="https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_1557159142.html" rel="noopener noreferrer"&gt;strict concurrency limits&lt;/a&gt; that require careful request throttling to avoid timeouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom field handling&lt;/strong&gt;: NetSuite's custom fields require specific formatting and field ID mapping that varies by account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling complexity&lt;/strong&gt;: NetSuite's error responses require specific parsing and retry logic for different error types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data transformation&lt;/strong&gt;: NetSuite's data structures often don't match your application's data models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagination complexity&lt;/strong&gt;: Handling large datasets requires implementing cursor-based pagination logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook limitations&lt;/strong&gt;: NetSuite's webhook support is limited, requiring polling for real-time data needs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these challenges adds development time and increases the potential for bugs in your integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Apideck Unified Accounting API
&lt;/h2&gt;

&lt;p&gt;Integrating with multiple accounting systems, including NetSuite, can be overwhelming and time-consuming. Managing OAuth signatures, handling rate limits, and dealing with different data formats across various platforms requires months of development effort and ongoing maintenance.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck Unified Accounting API&lt;/a&gt; provides a single integration point for 20+ accounting platforms, including NetSuite. This approach abstracts away the complexities of individual APIs, simplifying the integration process.&lt;/p&gt;

&lt;p&gt;Key benefits of using the Apideck Unified Accounting API include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time data processing&lt;/strong&gt;: All API calls are processed in real-time, not batched, ensuring your data is always fresh&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed authentication with Vault&lt;/strong&gt;: &lt;a href="https://www.apideck.com/products/vault" rel="noopener noreferrer"&gt;Apideck Vault&lt;/a&gt; handles all OAuth flows and credential management, eliminating complex authentication setup for your users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified data model&lt;/strong&gt;: Consistent data structures across all 18+ accounting platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in rate limit management&lt;/strong&gt;: Automatic throttling and retry logic with exponential backoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook emulation&lt;/strong&gt;: Get push notifications from platforms that support webhooks, with Apideck emulating webhooks for platforms that don't&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;White-label user interface&lt;/strong&gt;: Easily embedded interface that gives users a simple and secure connection experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data normalization&lt;/strong&gt;: Messy, inconsistent APIs are normalized into a single structure while still exposing raw downstream information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced maintenance burden&lt;/strong&gt;: Updates to underlying accounting systems don't require changes to your integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Making Requests with Apideck
&lt;/h2&gt;

&lt;p&gt;Before making API requests through Apideck, you need to &lt;a href="https://developers.apideck.com/connectors/netsuite/docs/consumer+connection" rel="noopener noreferrer"&gt;configure your NetSuite connection&lt;/a&gt; in the Apideck platform.&lt;/p&gt;

&lt;p&gt;Here's how simple the same operations become with Apideck's unified API. You can try these requests in our Api Explorer, which makes this even simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-apideck-app-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR-APP-ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &amp;lt;YOUR-API-KEY&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-apideck-consumer-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;CONSUMER-ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your NetSuite consumer&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Get a customer - unified across all accounting platforms&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`https://unify.apideck.com/accounting/customers/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error retrieving customer:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Create a customer - same API call works for NetSuite, QuickBooks, Xero, etc.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unify.apideck.com/accounting/customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error creating customer:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// List all invoices with automatic pagination&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getInvoices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unify.apideck.com/accounting/invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error retrieving invoices:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage examples&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Acme Corporation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contact@acme.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;+1-555-0123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newCustomer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Created customer:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createdCustomer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getInvoices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoices:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here’s an example of the same request from Apideck’s &lt;a href="https://developers.apideck.com/api-explorer" rel="noopener noreferrer"&gt;API Explorer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can test and make API calls with the simple drag and drop UI, which can easily pre-fill the response headers and body with the required data. You can see the &lt;code&gt;list customers&lt;/code&gt; request being done in a single shot.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/3Ab7l6AtTlpDsnWpkSLR8g/1ed51da4e0a1067e83e945be628dbdc5/Screenshot_2025-08-28_at_03.27.39_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/3Ab7l6AtTlpDsnWpkSLR8g/1ed51da4e0a1067e83e945be628dbdc5/Screenshot_2025-08-28_at_03.27.39_2x.png" alt="Screenshot 2025-08-28 at 03.27.39@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare this clean, simple code to the complex OAuth signature generation and authentication handling required for direct NetSuite integration. With Apideck, the same code works across NetSuite, QuickBooks, Xero, Sage, and dozens of other accounting platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Integration Examples
&lt;/h2&gt;

&lt;p&gt;Here are some practical examples showing how Apideck simplifies common NetSuite integration scenarios. Please note, you can try them out from our Api Explorer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-commerce Order Sync:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sync new orders from your e-commerce platform to NetSuite&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncOrderToNetSuite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;unit_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="na"&gt;due_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unify.apideck.com/accounting/invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Customer Data Synchronization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Keep customer data in sync between your CRM and NetSuite&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncCustomerData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;billing_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;line1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billing_street&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billing_city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billing_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;postal_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billing_zip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crmCustomer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;billing_country&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unify.apideck.com/accounting/customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this guide, you learned how to integrate with the NetSuite REST API and discovered the complexities involved in direct integration. As demonstrated, working directly with NetSuite's API requires managing complex OAuth 1.0a signatures, handling rate limits, dealing with custom field mappings, and maintaining error-prone authentication code.&lt;/p&gt;

&lt;p&gt;These challenges multiply when you need to support multiple accounting platforms beyond NetSuite. Each system has its own authentication methods, data structures, and API quirks that require separate implementations and ongoing maintenance.&lt;/p&gt;

&lt;p&gt;To simplify your accounting integrations and eliminate the complexities of direct API integration, consider using the &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck Unified Accounting API&lt;/a&gt; with the &lt;a href="https://www.apideck.com/connectors/netsuite" rel="noopener noreferrer"&gt;NetSuite Connector&lt;/a&gt;. By providing a single, consistent interface across all major accounting platforms, Apideck simplifies development, reduces maintenance overhead, and saves valuable development time and resources.&lt;/p&gt;

&lt;p&gt;With Apideck, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to NetSuite and 20+ other accounting platforms with the same code&lt;/li&gt;
&lt;li&gt;Skip complex authentication implementations
&lt;/li&gt;
&lt;li&gt;Get standardized data models across all platforms&lt;/li&gt;
&lt;li&gt;Benefit from built-in rate limiting and error handling&lt;/li&gt;
&lt;li&gt;Access real-time data processing (not batch processing)&lt;/li&gt;
&lt;li&gt;Use webhook emulation for platforms that don't natively support webhooks&lt;/li&gt;
&lt;li&gt;Focus on building features instead of managing integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://platform.apideck.com/api/auth/login?product=unify&amp;amp;screenHint=signup&amp;amp;connector=netsuite" rel="noopener noreferrer"&gt;Sign up for Apideck&lt;/a&gt; today to start simplifying your NetSuite and accounting integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's the difference between NetSuite REST API and SOAP API?
&lt;/h3&gt;

&lt;p&gt;The REST API is newer (2019), uses JSON, and is easier to implement, but has limited functionality coverage. The SOAP API has been around longer, offers broader NetSuite feature access, but requires XML handling and is more complex to implement. For bulk operations and advanced features, SOAP is often better. For modern web/mobile apps and simple CRUD operations, REST is preferred. See our &lt;a href="https://www.apideck.com/blog/guide-to-integrating-with-the-netsuite-soap-api" rel="noopener noreferrer"&gt;SOAP API guide&lt;/a&gt; for a detailed comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I handle NetSuite's rate limits?
&lt;/h3&gt;

&lt;p&gt;NetSuite uses concurrency-based rate limiting rather than traditional rate limits. Each account has a limit on concurrent API requests (typically 10-25 concurrent requests). When you exceed this limit, you'll get timeout errors rather than 429 rate limit errors. Implement exponential backoff retry logic and consider queuing requests during high-traffic periods. Monitor your usage through NetSuite's API monitoring tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use OAuth 2.0 with NetSuite REST API?
&lt;/h3&gt;

&lt;p&gt;Yes, NetSuite supports OAuth 2.0 for REST API authentication, but Token-Based Authentication (TBA) is more commonly used because tokens don't expire like OAuth 2.0 access tokens do. OAuth 2.0 requires refresh token management and has a 7-day refresh limit, making TBA more suitable for server-to-server integrations. However, if you need user-facing authentication flows, OAuth 2.0 is the better choice.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>tutorial</category>
      <category>backend</category>
    </item>
    <item>
      <title>DATEV API Integration: A Comprehensive Technical Guide</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Fri, 31 Oct 2025 14:27:33 +0000</pubDate>
      <link>https://dev.to/apideck/datev-api-integration-a-comprehensive-technical-guide-24l6</link>
      <guid>https://dev.to/apideck/datev-api-integration-a-comprehensive-technical-guide-24l6</guid>
      <description>&lt;p&gt;When German businesses need accounting software, they turn to DATEV. With decades of market dominance and adoption by thousands of tax consultants across Germany, DATEV has become the de facto standard for financial record-keeping in the German market. But here's where things get interesting for developers: integrating with DATEV isn't like connecting to your typical REST API. The architecture is fundamentally different, and understanding these differences is critical to building a successful integration.&lt;/p&gt;

&lt;p&gt;If you're building an ERP system, e-commerce platform, or any business application that needs to sync financial data with German accounting workflows, you'll need to understand how DATEV integration actually works. This guide will walk you through the technical architecture, explain why DATEV takes a batch-processing approach instead of real-time REST calls, and show you the practical steps for implementing a robust integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding DATEV API Integration
&lt;/h2&gt;

&lt;p&gt;DATEV API integration connects your third-party platform to DATEV's accounting software ecosystem, enabling automated synchronization of financial data without manual file imports or error-prone copy-paste workflows. When you integrate with DATEV, you're connecting to the systems used by tax consultants and accountants across Germany to manage client finances, prepare tax filings, and maintain compliance with German accounting regulations.&lt;/p&gt;

&lt;p&gt;The typical use cases span multiple business domains. E-commerce platforms sync order data and payment records. Banking systems push transaction updates for reconciliation. Property management software sends rent payments and tenant invoicing data. Payroll systems transfer employee expense information. In each case, the goal is the same: eliminate manual data entry, reduce errors, and enable real-time visibility into financial operations.&lt;/p&gt;

&lt;p&gt;What makes DATEV integration valuable is the automation of repetitive accounting tasks. Instead of your accountant manually importing CSV files and checking for discrepancies, your integration automatically creates properly formatted records that flow directly into the accounting workflow. Invoice data, customer and supplier records, journal entries, cost center allocations, and VAT calculations all sync automatically. The result is enhanced compliance with German accounting regulations, reduced data errors, improved accuracy, and proactive financial management based on current data rather than week-old exports.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Types of DATEV APIs
&lt;/h2&gt;

&lt;p&gt;Here's where DATEV diverges sharply from typical API architectures. DATEV offers two primary data exchange formats, and neither one works like the REST or SOAP APIs you're probably familiar with. Understanding this distinction is absolutely critical before you start building your integration.&lt;/p&gt;

&lt;p&gt;The CSV-based API, used for DATEV Rechnungswesen (abbreviated as ReWe), handles finalized financial bookings and accounting records. When you need to submit completed transactions that are ready for the permanent accounting record, you use this approach. Data flows through flat, tabular CSV files that must follow precise column formatting requirements. These files are processed through batch jobs called EXTF-Jobs. This approach is common in on-premise environments and legacy integrations where the infrastructure was built before cloud-native architectures became standard.&lt;/p&gt;

&lt;p&gt;The XML-based API, used for DATEV Unternehmen Online (abbreviated as DUo), handles booking suggestions and invoice processing. This format supports complex, hierarchical data structures that represent more nuanced financial information. Data moves through structured XML files that are validated against XSD schemas to ensure compliance. These files are processed with batch jobs called dxso-Jobs. The XML approach focuses on cloud platforms and modern workflows, particularly for scenarios where you're proposing bookings that require accountant review before finalization.&lt;/p&gt;

&lt;p&gt;Now here's the critical point that trips up many developers: DATEV does &lt;strong&gt;not&lt;/strong&gt; use traditional REST or SOAP APIs for core accounting operations. If you're expecting to make HTTP POST requests with JSON payloads and get immediate synchronous responses, you're approaching this wrong. DATEV's integration relies on asynchronous batch-based file processing. You submit files as jobs, those jobs enter a processing queue, and DATEV systems handle them on their own schedule. You cannot use typical RESTful calls for submitting bookings or financial records.&lt;/p&gt;

&lt;p&gt;There are some exceptions worth noting. DATEV does offer REST endpoints for certain niche products. The Cash Register Import API (MeinFiskal) provides documented REST endpoints for importing electronic cash register data. The Document Management system integrates via REST APIs through DATEVconnect for document workflows. But these are specialized use cases, not the core accounting functionality.&lt;/p&gt;

&lt;p&gt;Third-party unified API providers sometimes expose REST endpoints that make DATEV integration feel more familiar to developers. But understand what's happening under the hood: these are wrappers that translate your REST calls into DATEV's batch processing behind the scenes. The underlying architecture remains batch-based, even if the developer experience feels more REST-like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with the accounting:documents API
&lt;/h2&gt;

&lt;p&gt;Let me show you a practical example using the accounting:documents REST API, which handles document uploads to Belege online in DATEV Unternehmen online. This is one of the REST endpoints that DATEV provides, and it demonstrates the authentication and data handling patterns you'll encounter.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/3YCIyGW6jYowcSsVJpq6qe/3362763c323a3589ba4261711e9fcdc5/Screenshot_2025-10-09_at_02.43.54_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/3YCIyGW6jYowcSsVJpq6qe/3362763c323a3589ba4261711e9fcdc5/Screenshot_2025-10-09_at_02.43.54_2x.png" alt="Screenshot 2025-10-09 at 02.43.54@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, you need to retrieve the list of clients your authenticated user can access. This establishes which companies you have permission to integrate with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://accounting-documents.api.datev.de/platform/v2/clients
Authorization: Bearer {access_token}
X-DATEV-Client-Id: {your_client_id}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response gives you client identifiers that you'll use in subsequent requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"client_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"consultant_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;455148&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"455148-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Muster GmbH 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;id&lt;/code&gt; field is the technical identifier you'll use for all data operations. Notice that it combines the consultant number and client number, creating a unique reference for this specific client within the DATEV system.&lt;/p&gt;

&lt;p&gt;Before uploading documents, you should check what document types are available for this client. Document types determine how DATEV processes and categorizes your uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://accounting-documents.api.datev.de/platform/v2/clients/455148-1/document-types
Authorization: Bearer {access_token}
X-DATEV-Client-Id: {your_client_id}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the configured document types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rechnungseingang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invoices_received"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"debit_credit_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debit"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rechnungsausgang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"outgoing_invoices"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"debit_credit_identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"credit"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kasse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"other_documents"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can upload a document. This uses multipart form data to combine the file with metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://accounting-documents.api.datev.de/platform/v2/clients/455148-1/documents
Authorization: Bearer {access_token}
X-DATEV-Client-Id: {your_client_id}
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="file"; filename="invoice-2024-001.pdf"
Content-Type: application/pdf

[Binary PDF content]
--boundary
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{
  "document_type": "Rechnungseingang",
  "note": "Supplier invoice for office supplies",
  "category": "MyERP",
  "folder": "Invoices",
  "register": "2024/10"
}
--boundary--
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The metadata structure organizes documents within DATEV's document repository using a three-level hierarchy: category, folder, and register. If you specify any of these levels, you must provide all three. This organizational structure helps accountants locate documents efficiently within the DATEV interface.&lt;/p&gt;

&lt;p&gt;When the upload succeeds, you receive confirmation with file details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invoice-2024-001.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;125742&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"upload_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-10-09T14:23:12+02:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"media_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/pdf"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"document_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rechnungseingang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Supplier invoice for office supplies"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authentication and Authorization
&lt;/h2&gt;

&lt;p&gt;DATEV uses OAuth2 for authorization, particularly for cloud-based integrations with Unternehmen Online. You need to design secure user consent workflows that clearly explain what data your application will access and how it will be used. Many integration scenarios require third-party approval or validation from the tax consultant who manages the client's account, since tax consultants control client access within the DATEV ecosystem.&lt;/p&gt;

&lt;p&gt;Your OAuth implementation should support long-lived tokens with automatic renewal processes. This is critical because DATEV integrations often need continuous data synchronization without requiring users to re-authenticate frequently. Users expect their accounting data to flow automatically once they've granted initial consent.&lt;/p&gt;

&lt;p&gt;The typical OAuth flow follows this pattern: redirect the user to DATEV's authorization endpoint, receive an authorization code after the user grants consent, exchange that code for access and refresh tokens, and use the access token for API requests. When the access token expires, use the refresh token to obtain a new access token without requiring user interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Format Compliance and Validation
&lt;/h2&gt;

&lt;p&gt;DATEV enforces strict format requirements because accounting data must maintain audit compliance and regulatory standards. The CSV and XML formats are fixed specifications that vary by product. You cannot choose arbitrary formats or invent your own column structures.&lt;/p&gt;

&lt;p&gt;For XML submissions to Unternehmen Online, your files must validate against DATEV's XSD schemas before processing will succeed. The schemas define required fields, data types, value constraints, and structural relationships. XML validation failures result in immediate job rejection with error codes that indicate which validation rules you violated.&lt;/p&gt;

&lt;p&gt;CSV files for Rechnungswesen demand precise column formatting. Each column must contain the correct data type in the expected position. Column order matters. Field delimiters must match the specification exactly. Even minor deviations cause processing failures because the DATEV import engine cannot risk misinterpreting financial data.&lt;/p&gt;

&lt;p&gt;Beyond basic format validation, you must properly structure metadata that accompanies your financial records. Cost center allocations, voucher numbers, analytic account references, and VAT information all need to follow DATEV's coding schemes. German VAT codes, for instance, follow specific conventions that DATEV recognizes and validates against current tax regulations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Error Scenarios
&lt;/h2&gt;

&lt;p&gt;The OpenAPI specification documents numerous error codes you'll encounter. Let me walk through the most common ones and what they mean for your integration.&lt;/p&gt;

&lt;p&gt;Error &lt;code&gt;#DCO01010 restapi.InvalidDocumentTypeFault&lt;/code&gt; occurs when you specify a document type that doesn't exist for the client. Remember that each client configures their own document types. You must query the available types first and only use values from that list. Don't assume standard document types exist universally.&lt;/p&gt;

&lt;p&gt;Error &lt;code&gt;#DCO01015 restapi.UnsupportedFileTypeFault&lt;/code&gt; indicates the file format isn't accepted. DATEV restricts uploads to specific file types for security and compatibility reasons. The allowed formats vary between DuoNext and legacy systems. Always check the permitted file extensions for your target environment.&lt;/p&gt;

&lt;p&gt;Error &lt;code&gt;#DCO01016 restapi.FileSizeFault&lt;/code&gt; means your file exceeds the twenty-megabyte limit. Large files create processing and storage burdens. If you're handling oversized documents, you'll need to compress them, split them into multiple uploads, or reconsider your document workflow. DATEV recommends warning users when files exceed 500 KB because even allowed file sizes can slow down system performance.&lt;/p&gt;

&lt;p&gt;Error &lt;code&gt;#DCO01253 restapi.DuplicateFileFault&lt;/code&gt; signals that a file with identical name and document type already exists in Belege Online. DATEV prevents duplicate uploads to avoid confusion. Your integration should either generate unique filenames, allow users to specify different document types for duplicates, or implement deduplication logic before attempting uploads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Approaches: Direct vs. Unified APIs
&lt;/h2&gt;

&lt;p&gt;You face a fundamental architectural decision: integrate directly with DATEV or use a unified API provider that abstracts the complexity. Each approach has distinct tradeoffs that affect your development timeline, maintenance burden, and feature coverage.&lt;/p&gt;

&lt;p&gt;Direct integration with DATEV gives you complete control over the integration logic. You're not dependent on third-party service availability or pricing changes. You can access all DATEV features without waiting for a middleware provider to expose them. But this control comes with significant complexity. You need deep understanding of CSV and XML format specifications. You're responsible for all batch job management, status monitoring, error handling, and retry logic. Testing requires thorough validation across different client configurations and fiscal year setups. Development cycles stretch longer because you're implementing the entire integration stack yourself. Ongoing maintenance becomes a permanent operational burden as DATEV updates their APIs and format requirements.&lt;/p&gt;

&lt;p&gt;Unified API providers like Chift, Maesn, and others offer a RESTful developer experience that feels familiar to modern web developers. These platforms automatically handle batch job creation and management behind the scenes. They provide built-in error handling and retry logic that's been tested across many production deployments. Most importantly, they support multiple accounting platforms beyond DATEV, so your integration code can potentially connect to QuickBooks, Xero, NetSuite, and other systems using the same API patterns. Time to market drops dramatically because you're leveraging pre-built infrastructure. Maintenance overhead shifts to the unified API provider.&lt;/p&gt;

&lt;p&gt;The downsides of unified APIs include dependency on third-party service reliability and additional costs beyond DATEV's pricing. Not all DATEV features may be exposed through the unified API wrapper, potentially limiting your integration's capabilities. You're also adding network hops and processing delays compared to direct DATEV communication.&lt;/p&gt;

&lt;p&gt;For most development teams, especially those building multi-platform integrations or operating under tight deadlines, unified API providers offer the better path. The productivity gains and reduced maintenance burden usually outweigh the additional costs and potential feature limitations. Direct DATEV integration makes sense primarily for specialized use cases that require features not available through unified APIs, or for large-scale deployments where the volume justifies investing in custom infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Implementation Considerations
&lt;/h2&gt;

&lt;p&gt;When you start implementing your DATEV integration, several practical concerns will surface immediately. File naming conventions matter more than you might expect. DATEV recommends UTF-8 encoding with precomposed Unicode characters. Decomposed characters cause problems. Special characters like umlauts must be single code points, not multi-byte sequences. DATEV maintains a whitelist of permitted characters in filenames, and unsupported characters get replaced with underscores automatically. This can create confusion if your system generates filenames with characters outside the whitelist.&lt;/p&gt;

&lt;p&gt;Rate limiting and batch processing constraints affect how you design your data synchronization strategy. You cannot stream continuous updates in real-time. Instead, you batch operations and submit them periodically. Determine appropriate batch sizes that balance efficiency against error handling complexity. Larger batches process more efficiently but make error diagnosis harder when something fails.&lt;/p&gt;

&lt;p&gt;Error recovery needs thoughtful design because batch processing creates asynchronous failure modes. When a job fails, you need to detect the failure, parse error messages, determine which records caused problems, and decide whether to retry the entire batch or just the failed records. Your integration should implement exponential backoff for transient failures while permanently failing for validation errors that won't resolve through retries.&lt;/p&gt;

&lt;p&gt;Testing strategies require access to DATEV sandbox environments. The API specification shows separate endpoints for sandbox testing. Use these extensively before deploying to production. Test with various client configurations, different fiscal year setups, and edge cases in your data. Remember that DATEV may automatically generate subsequent fiscal years under certain conditions, so test your integration's behavior when fiscal year transitions occur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Recommendations
&lt;/h2&gt;

&lt;p&gt;DATEV integration is fundamentally different from typical REST API integration work. The batch-processing architecture, strict format requirements, and certification processes create complexity that shouldn't be underestimated. Before starting development, evaluate your technical resources honestly. Do you have developers with experience in batch processing systems? Can you invest time in understanding DATEV's specific format requirements? Do you need features that unified API providers don't expose?&lt;/p&gt;

&lt;p&gt;For most teams, starting with a unified API provider offers the fastest path to a working integration. You can always migrate to direct DATEV integration later if specific requirements demand it. Focus initially on understanding the business workflows, data mapping requirements, and error handling strategies. These concerns remain constant regardless of whether you integrate directly or through middleware.&lt;/p&gt;

&lt;p&gt;Document your integration thoroughly, especially the format specifications and validation requirements. Future developers maintaining your integration will need this context. Build comprehensive error logging from day one because diagnosing batch processing failures requires detailed audit trails.&lt;/p&gt;

&lt;p&gt;DATEV integration enables powerful accounting automation, but success requires respecting the architectural constraints and compliance requirements that make DATEV the trusted standard for German accounting workflows.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Bank Feeds API Integration: Why You Can’t Afford to Skip This Feature</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Fri, 31 Oct 2025 14:27:10 +0000</pubDate>
      <link>https://dev.to/apideck/bank-feeds-api-integration-why-you-cant-afford-to-skip-this-feature-f22</link>
      <guid>https://dev.to/apideck/bank-feeds-api-integration-why-you-cant-afford-to-skip-this-feature-f22</guid>
      <description>&lt;p&gt;Your corporate card product is brilliant. Your business banking app solves real problems. Your expense management platform beats the competition on features and pricing.&lt;/p&gt;

&lt;p&gt;But then a prospect asks: "Does this sync with our Xero account?"&lt;/p&gt;

&lt;p&gt;And suddenly, you're explaining why users need to export CSVs and manually upload bank statements to their accounting system. The demo goes cold. The deal stalls. Your competitor with the bank feed integration wins the business.&lt;/p&gt;

&lt;p&gt;This happens every day to fintech companies that treat bank feeds as a "nice to have" feature. The reality? For finance teams, bank feeds are table stakes. Without them, you're asking users to do work that could be automated via an API integration.&lt;/p&gt;

&lt;p&gt;Here's how to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Bank Feeds Actually Do (And Why Your Customers Demand Them)
&lt;/h2&gt;

&lt;p&gt;Bank feeds automatically send transaction data from your platform into users' accounting systems, removing the need for CSV uploads and manual entry. The data flows directly from your product into Xero, QuickBooks, or NetSuite as transactions happen.&lt;/p&gt;

&lt;p&gt;The primary reason finance teams care about bank feeds is reconciliation. When transactions appear automatically in accounting software, users can match them against invoices, bills, and other entries to close their books faster and with more confidence.&lt;/p&gt;

&lt;p&gt;Manual reconciliation involves downloading a CSV or OFX file, reforming the columns, uploading it to their accounting system, and then manually matching each transaction. This takes hours. It creates errors. Finance teams hate it.&lt;/p&gt;

&lt;p&gt;Bank feeds eliminate all of that. The transaction happens. Data appears in the accounting system. User clicks to reconcile. Done.&lt;/p&gt;

&lt;p&gt;Bank feeds are especially valuable for financial platforms that handle money movement: corporate card providers, business banking apps, expense tools, payout platforms, and accounting automation products. If your product processes transactions on behalf of business customers, those transactions need to flow into their ledger.&lt;/p&gt;

&lt;p&gt;While fintechs offer great integrations for accounting software, they traditionally lack access to the resources, APIs, and partnerships required to provide the "bank-like" experience small businesses expect. That's changing, but the expectation exists. Your users want the same experience they get from their bank: automatic feeds, no manual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Use Cases That Actually Drive Revenue
&lt;/h2&gt;

&lt;p&gt;Let's get specific about where bank feeds matter most.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/4bUNQXXiGi116xhdVSJOGn/8a964ca4e3079a8bbf665e2269028b63/Screenshot_2025-10-01_at_03.09.37_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/4bUNQXXiGi116xhdVSJOGn/8a964ca4e3079a8bbf665e2269028b63/Screenshot_2025-10-01_at_03.09.37_2x.png" alt="Screenshot 2025-10-01 at 03.09.37@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Corporate Cards and Expense Management&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Your users swipe corporate cards. Those transactions need to appear in their accounting system the same day, not next week, after someone exports a spreadsheet.&lt;/p&gt;

&lt;p&gt;For complete reconciliation, you need two integrations: accounting sync for expenses AND bank feeds for the actual bank transactions. Without both, users still do manual work.&lt;/p&gt;

&lt;p&gt;Example: Employee buys office supplies with the company card. Transaction hits your platform. Bank feed sends it to QuickBooks. The finance team opens QuickBooks, sees the transaction, and matches it to the expense report. Approved in 30 seconds instead of 30 minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Business Banking and Neobanks&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Fintechs can deliver a bank-like experience by automating the upload of transaction statements to the general ledger for reconciliation. This is what banks do. If you're competing with banks, you need to do it too.&lt;/p&gt;

&lt;p&gt;Arc, a fintech company serving startups with treasury management and revenue-based financing, put it clearly: "We offer customers deposit accounts that serve as the core of how they run their business. It's critical for our customers to sync that data to their accounting systems."&lt;/p&gt;

&lt;p&gt;Without bank feeds, your business banking app feels incomplete. With them, you're offering the full package.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Bill Pay and AP Automation&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Push bill pay transactions to accounting systems for real-time payables reconciliation. When your platform pays a vendor invoice, that payment should appear in the customer's accounting system immediately.&lt;/p&gt;

&lt;p&gt;This closes the loop on accounts payable. Your app initiates payment. Bank feed confirms it happened. The accounting system marks the bill as paid. No manual matching required.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Treasury Management&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Treasury teams need real-time cash position visibility across multiple bank accounts. Bank feeds provide the data they need to forecast cash flow, manage liquidity, and make funding decisions.&lt;/p&gt;

&lt;p&gt;For companies managing cash across subsidiaries or multiple currencies, automated bank feeds make daily treasury operations possible without building custom integrations for every bank account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Direct Integration Is a Nightmare
&lt;/h2&gt;

&lt;p&gt;You might think: "We'll just build a direct integration with Xero. How hard can it be?"&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/lbpnnIB4RCr2kEnfvqqxX/5a29ec3dd7a0abdf18969c7724a7ea8c/ChatGPT_Image_Oct_1__2025__02_01_07_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/lbpnnIB4RCr2kEnfvqqxX/5a29ec3dd7a0abdf18969c7724a7ea8c/ChatGPT_Image_Oct_1__2025__02_01_07_AM.png" alt="ChatGPT Image Oct 1, 2025, 02 01 07 AM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very hard. Here's why.&lt;/p&gt;

&lt;p&gt;Xero's Bank Feeds API is a closed API only available to financial institutions with an established partnership with Xero. You can't just sign up and start using it. You need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Apply to become a Xero financial services partner&lt;/li&gt;
&lt;li&gt;Wait for approval (no guarantees)&lt;/li&gt;
&lt;li&gt;Go through certification process (takes months)&lt;/li&gt;
&lt;li&gt;Get your application certified for bank feeds access&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's just for Xero. Now repeat this process for QuickBooks, NetSuite, Sage Intacct, MYOB, and every other accounting platform your customers use.&lt;/p&gt;

&lt;p&gt;Each platform has different APIs. Different authentication flows. Different data models. Different certification requirements. Different ongoing maintenance needs.&lt;/p&gt;

&lt;p&gt;The math gets ugly fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3-6 months per integration&lt;/li&gt;
&lt;li&gt;5 major platforms&lt;/li&gt;
&lt;li&gt;= 15-30 months of development time&lt;/li&gt;
&lt;li&gt;Plus ongoing maintenance when APIs change&lt;/li&gt;
&lt;li&gt;Plus customer support for auth issues&lt;/li&gt;
&lt;li&gt;Plus compliance and security reviews&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And during those 15-30 months, you're not building your core product. You're managing OAuth flows and debugging webhook failures.&lt;/p&gt;

&lt;p&gt;Most fintech companies don't have that time. The market moves too fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Apideck Solves the Bank Feed Problem
&lt;/h2&gt;

&lt;p&gt;A unified API aggregates multiple APIs in the same category into a single standardized endpoint with unified authentication and normalized data models. Instead of building 20 separate integrations, you build one.&lt;/p&gt;

&lt;p&gt;For bank feeds, this means:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;One Integration, Multiple Platforms&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Apideck's Accounting API provides a single REST interface to sync data across 20+ accounting platforms. You write your bank feed integration once. It works with Xero, QuickBooks, NetSuite, Sage Intacct, and dozens of other systems.&lt;/p&gt;

&lt;p&gt;Your code stays the same. Apideck handles the platform-specific details.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Authentication That Actually Works&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Apideck Vault handles OAuth flows and stores credentials securely with enterprise-grade security. Your users connect their accounting system through a white-label interface. They never see your API keys. You never manage their tokens.&lt;/p&gt;

&lt;p&gt;The same authentication flow works for every platform. No custom OAuth implementations per provider.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Maintenance You Don't Have to Do&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Apideck handles connector updates, API versioning, and provider-specific changes without requiring code changes in your application. When QuickBooks ships a breaking change or Xero updates authentication requirements, Apideck updates the connector. Your code keeps working.&lt;/p&gt;

&lt;p&gt;This is huge. Direct integrations break constantly. APIs change. Partners add new requirements. Version deprecations happen with 60 days' notice. With Apideck, that's not your problem anymore.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Normalized Data Models&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Apideck normalizes messy, inconsistent APIs into a single structure while still exposing raw downstream information. Every platform uses different field names and data formats. Apideck translates between your standard format and whatever the downstream API expects.&lt;/p&gt;

&lt;p&gt;You send the same JSON payload for bank transactions. Apideck converts it to QBXML for QuickBooks Desktop, REST for Xero, or whatever format NetSuite needs. You don't care about implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Implementation: Xero Bank Feeds with Apideck
&lt;/h2&gt;

&lt;p&gt;Let's get into the actual code. Here's how you implement bank feeds for Xero using Apideck's unified API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You still need Xero Bank Feeds API certification. Apideck can't bypass that requirement because it's a partnership between you and Xero. But once you're certified, Apideck handles everything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a Bank Feed Account&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Bank Feed Accounts endpoint in Apideck's Accounting API corresponds to Xero's Feed Connections API and links your platform's accounts to bank or credit card accounts in Xero.&lt;/p&gt;

&lt;p&gt;POST to &lt;a href="https://unify.apideck.com/accounting/bank-feed-accounts" rel="noopener noreferrer"&gt;https://unify.apideck.com/accounting/bank-feed-accounts&lt;/a&gt; with this payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"corp-card-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_account_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345676"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_account_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Corporate Card Account"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bank_account_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the bank account already exists in Xero, use target_account_id instead of name and number to link it. If not, provide target_account_name and target_account_number so Xero creates a new account.&lt;/p&gt;

&lt;p&gt;The response gives you a bank_feed_account_id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"55af487b-df2b-425e-9432-ea2db7a77481"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store this ID. You'll need it for every transaction batch you send.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Send Bank Transactions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Bank Feed Statements endpoint lets you send a batch of transactions tied to the bank_feed_account_id created earlier.&lt;/p&gt;

&lt;p&gt;POST to &lt;a href="https://unify.apideck.com/accounting/bank-feed-statements:" rel="noopener noreferrer"&gt;https://unify.apideck.com/accounting/bank-feed-statements:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bank_feed_account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"55af487b-df2b-425e-9432-ea2db7a77481"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-05-01T00:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-05-15T23:59:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_balance_credit_or_debit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"credit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;142.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end_balance_credit_or_debit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"credit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posted_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-05-02T01:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Office Supplies - Staples"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"credit_or_debit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source_transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_1019"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"counterparty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Staples Inc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INV-20250502"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"posted_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-05-03T09:30:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Client Payment - ACME Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;89.23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"credit_or_debit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"credit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source_transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_1020"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"counterparty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACME Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoice-445"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payment"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Best practices: Send statements in chronological order, ensure balances align correctly (Xero validates them), and avoid gaps or overlaps in statement periods to prevent rejections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Users Reconcile in Xero&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the statement is successfully uploaded, transactions appear in Xero under the connected bank account where users reconcile them using Xero's standard process by matching each line to invoices, bills, or other records.&lt;/p&gt;

&lt;p&gt;This creates the workflow your finance users expect: your platform sends transactions, Xero handles reconciliation. By automating this flow, you help users maintain accurate books with minimal manual effort.&lt;/p&gt;

&lt;p&gt;You can send feeds daily, weekly, or on-demand. Most products send daily updates to keep accounting systems current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Xero: Multi-Platform Support
&lt;/h2&gt;

&lt;p&gt;Xero is live and fully supported. But your customers use other accounting systems too, we're actively adding support for more platforms.&lt;/p&gt;

&lt;p&gt;SaaS companies typically need multiple accounting integrations to serve diverse customer bases: UK-focused customers use Xero, US SMBs prefer QuickBooks, and enterprise clients require Sage Intacct.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/4yILhoGDxksQwPNvnMMYqL/3349a6d770059fd088f39d8d4dfb1d1f/Screenshot_2025-10-01_at_01.46.49_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/4yILhoGDxksQwPNvnMMYqL/3349a6d770059fd088f39d8d4dfb1d1f/Screenshot_2025-10-01_at_01.46.49_2x.png" alt="Screenshot 2025-10-01 at 01.46.49@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apideck supports 20+ accounting platforms, including QuickBooks, NetSuite, Sage Intacct, MYOB, Microsoft Dynamics 365 Business Central, FreshBooks, and Exact Online. The same unified API works across all of them.&lt;/p&gt;

&lt;p&gt;Different platforms use different formats for bank feed data. While Xero uses its proprietary Bank Feeds API, NetSuite commonly relies on the OFX (Open Financial Exchange) format for importing bank statements. OFX is a standardized format that many banks and financial institutions use to exchange transaction data. If you're building for NetSuite users, understanding OFX formatting becomes important for smooth data imports. Learn more about importing bank statements into NetSuite using OFX format at &lt;a href="https://www.docuclipper.com/blog/how-to-import-bank-statement-into-netsuite/" rel="noopener noreferrer"&gt;DocuClipper's guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Right now, bank feeds are fully implemented for Xero. Support for additional platforms is expanding. The architecture is already there. The API endpoints are the same. As Apideck adds bank feed support to more connectors, your integration automatically gains access to those platforms.&lt;/p&gt;

&lt;p&gt;This matters because you can't predict which accounting system your next big customer will use. Building flexibility into your integration strategy now saves months of work later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line for Decision Makers
&lt;/h2&gt;

&lt;p&gt;Bank feeds separate products that finance teams actually adopt from products that get evaluated and rejected.&lt;/p&gt;

&lt;p&gt;If you're building a fintech product that handles money movement, you need bank feeds. Not eventually. Now.&lt;/p&gt;

&lt;p&gt;The choice is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build direct integrations to 5-10 accounting platforms over 18-24 months&lt;/li&gt;
&lt;li&gt;Use a unified API and ship bank feeds in a few weeks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Development teams can implement accounting integrations in weeks rather than months using unified APIs with standardized webhooks, consistent error handling, and unified documentation that eliminates the complexity of learning multiple provider-specific APIs.&lt;/p&gt;

&lt;p&gt;Apideck gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One integration for 20+ accounting platforms&lt;/li&gt;
&lt;li&gt;Managed authentication and credential storage&lt;/li&gt;
&lt;li&gt;Automatic handling of API changes and updates&lt;/li&gt;
&lt;li&gt;Production-ready bank feeds for Xero (with more platforms coming)&lt;/li&gt;
&lt;li&gt;Real-time data processing with no batching delays&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Ready to add bank feeds to your product?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Xero Bank Feeds:&lt;/strong&gt; Start with the detailed technical guide at &lt;a href="https://developers.apideck.com/guides/bank-feeds-xero" rel="noopener noreferrer"&gt;developers.apideck.com/guides/bank-feeds-xero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For the Full Accounting API:&lt;/strong&gt; Explore all endpoints and connectors at &lt;a href="https://developers.apideck.com/apis/accounting/reference" rel="noopener noreferrer"&gt;developers.apideck.com/apis/accounting&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To Try It Out:&lt;/strong&gt; Sign up for a free Apideck account and get 2,500 API calls to test your integration. No credit card required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To Talk Strategy:&lt;/strong&gt; Book a demo if you're evaluating unified APIs for your product roadmap. The Apideck team can walk you through your specific use case and integration requirements.&lt;/p&gt;

&lt;p&gt;The fintech companies winning deals right now are the ones that solved bank feeds months ago. Stop building one-off integrations. Ship bank feeds this quarter instead of next year.&lt;/p&gt;

&lt;p&gt;Your finance team users will thank you.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>backend</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Connect with the HubSpot API</title>
      <dc:creator>𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒</dc:creator>
      <pubDate>Thu, 30 Oct 2025 16:43:28 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-connect-with-the-hubspot-api-8hk</link>
      <guid>https://dev.to/apideck/how-to-connect-with-the-hubspot-api-8hk</guid>
      <description>&lt;p&gt;HubSpot's API looks modern on the surface. REST endpoints, JSON payloads, OAuth 2.0, webhooks. Then you actually build something with it and discover the truth: it's a maze of rate limits, undocumented quirks, and lifecycle stage logic that defies human comprehension.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2TPol60vMKzXgdQZyL6lb9/b349a2c22026974a0a7ba370e207128a/Screenshot_2025-09-12_at_23.42.05_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2TPol60vMKzXgdQZyL6lb9/b349a2c22026974a0a7ba370e207128a/Screenshot_2025-09-12_at_23.42.05_2x.png" alt="Screenshot 2025-09-12 at 23.42.05@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've spent the last three years integrating HubSpot for various clients. Here's what the documentation won't tell you and what will save you from the same pain I went through.&lt;/p&gt;

&lt;h2&gt;
  
  
  The OAuth Dance Nobody Warns You About
&lt;/h2&gt;

&lt;p&gt;HubSpot uses OAuth 2.0, which sounds standard until you realize their implementation has its own special flavor. You need three things before you even start: a developer account (separate from your regular HubSpot account), an app registration, and the patience of a saint.&lt;/p&gt;

&lt;p&gt;First, create your app at &lt;a href="https://developers.hubspot.com" rel="noopener noreferrer"&gt;developers.hubspot.com&lt;/a&gt;. You'll get a Client ID and Client Secret. Guard that secret like your life depends on it, because regenerating it will break every existing integration.&lt;/p&gt;

&lt;p&gt;Here's the authorization URL you need to build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://app.hubspot.com/oauth/authorize?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`client_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`redirect_uri=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`scope=crm.objects.contacts.read%20crm.objects.contacts.write`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait, there's a catch nobody mentions: the redirect URI must match EXACTLY what you registered. Not just the domain, not just the path, but every single character, including trailing slashes. Get it wrong and you'll see a generic error that tells you nothing.&lt;/p&gt;

&lt;p&gt;When the user approves, HubSpot redirects back with a code. You have exactly 30 seconds to exchange it for tokens, or it expires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.hubapi.com/oauth/v1/token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorization_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authCode&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh_token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most access tokens are short-lived. You can check the expires_in parameter when generating an access token to determine its lifetime (in seconds). Practically you have a few minutes before they expire.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/6HdfLyDkKs63LyiUHccWm5/13c29ebb8e281f37d4196a0488da410c/Screenshot_2025-09-12_at_23.42.53_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/6HdfLyDkKs63LyiUHccWm5/13c29ebb8e281f37d4196a0488da410c/Screenshot_2025-09-12_at_23.42.53_2x.png" alt="Screenshot 2025-09-12 at 23.42.53@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the refresh logic you'll need running constantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;refreshAccessToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.hubapi.com/oauth/v1/token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refresh_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;refreshToken&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Refresh token is dead, user needs to reauthorize&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token refresh failed - user must reauthorize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the refresh access token logic from the &lt;a href="https://developers.hubspot.com/docs/api-reference/auth-oauth-v1/tokens/post-oauth-v1-token" rel="noopener noreferrer"&gt;HubSpot documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limiting: The 429 Error Festival
&lt;/h2&gt;

&lt;p&gt;HubSpot's rate limits are documented, but what they don't tell you is how aggressive they are. You get 100 requests per 10 seconds for public apps. That sounds like a lot until you realize that's shared across ALL your users if you're using a single app registration.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2QqH4pzYEwzbgSBdvVBuk9/823027367fbf6ed0a827c04609753825/Screenshot_2025-09-13_at_22.07.28_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2QqH4pzYEwzbgSBdvVBuk9/823027367fbf6ed0a827c04609753825/Screenshot_2025-09-13_at_22.07.28_2x.png" alt="Screenshot 2025-09-13 at 22.07.28@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, you can upgrade the number of calls your app can make, which is based on your account subscription in the account it's installed in. Here's the plan details from the &lt;a href="https://developers.hubspot.com/docs/developer-tooling/platform/usage-guidelines#app-limits" rel="noopener noreferrer"&gt;HubSpot Documentation&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;[IMAGE: Rate limit table by subscription tier]&lt;/p&gt;

&lt;p&gt;Here's what a 429 error looks like when you hit the limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You have reached your secondly limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errorType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RATE_LIMIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"correlationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c033cdaa-2c40-4a64-ae48-b4cec88dad24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"policyName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEN_SECONDLY_ROLLING"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worst part? HubSpot counts requests that fail against your rate limit. So when you hit the limit and retry immediately, you're making it worse. You need exponential backoff or you'll be stuck in 429 hell forever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeHubSpotRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max retries exceeded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Exponential backoff: 1s, 2s, 4s, 8s, 16s&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retryCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Rate limited. Waiting &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms before retry...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;makeHubSpotRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HubSpot API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But here's the real kicker: if you're using Make.com, Zapier, or any integration platform, you're sharing rate limits with every other customer using their HubSpot connector. I've seen perfectly reasonable workflows fail at 2 AM because someone else's integration went haywire. The only solution is to create your own OAuth app and use their "advanced" connection option.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lifecycle Stage Nightmare
&lt;/h2&gt;

&lt;p&gt;HubSpot's lifecycle stages are supposed to track where contacts are in your sales process. In reality, they're a one-way street designed by someone who's never had to fix bad data.&lt;/p&gt;

&lt;p&gt;Lifecycle stages can only move forward by default. Lead to Customer? Fine. Customer back to Lead because they canceled? Nope. You have to clear the field first, then set the new value in a separate API call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This won't work - lifecycle stage can't go backwards&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lifecyclestage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lead&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Fails silently&lt;/span&gt;

&lt;span class="c1"&gt;// This is what you actually need&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lifecyclestage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Clear it first&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lifecyclestage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lead&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Now set it&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;makeHubSpotRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://api.hubapi.com/crm/v3/objects/contacts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PATCH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's two API calls for one field update, doubling your rate limit usage. And it gets worse: the batch API doesn't guarantee order, so you can't clear and set in the same batch request. Every backwards lifecycle stage movement costs you two separate API calls.&lt;/p&gt;

&lt;p&gt;Oh, and those "Became a [stage] date" properties? They're being deprecated. HubSpot announced this in 2024, but half of its documentation still references them. You now need to use their "calculated properties," which have their own special quirks and can't be set via API at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Properties: The False Promise
&lt;/h2&gt;

&lt;p&gt;HubSpot lets you create custom properties for anything. Sounds great until you realize that enumeration properties (dropdowns, checkboxes) have their own internal IDs that aren't the same as the labels you see in the UI.&lt;/p&gt;

&lt;p&gt;You think you're setting a property to "Enterprise Customer," but HubSpot wants "enterprise_customer_7821" or something equally ridiculous. To find the internal values, you need to query the properties endpoint first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;propertyResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;makeHubSpotRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.hubapi.com/crm/v3/properties/contacts/industry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Returns something like:&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   "options": [&lt;/span&gt;
&lt;span class="c1"&gt;//     { "label": "Enterprise Customer", "value": "enterprise_customer_7821" },&lt;/span&gt;
&lt;span class="c1"&gt;//     { "label": "SMB Customer", "value": "smb_customer_9183" }&lt;/span&gt;
&lt;span class="c1"&gt;//   ]&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And don't even think about changing these values later. Every integration, workflow, and report using that property will break. I learned this when a client wanted to rename "Hot Lead" to "Qualified Lead" and it took three days to fix all the broken automations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks: Death by a Thousand Subscriptions
&lt;/h2&gt;

&lt;p&gt;HubSpot webhooks seem straightforward: subscribe to events, receive notifications. What they don't tell you is that webhooks are tied to your app, not individual accounts. Every customer using your app shares the same webhook URL.&lt;/p&gt;

&lt;p&gt;Setting up webhooks requires a verified domain and HTTPS endpoint that can handle HubSpot's validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// HubSpot sends validation on setup&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-hubspot-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-hubspot-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sourceString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sourceString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Process webhook events&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for object &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objectId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// But which customer is this for? Good luck figuring that out&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The webhook payload doesn't include which account it's from. You get an object ID and have to make another API call (counting against your rate limit) to figure out whose data changed. With 100 customers, that's 100 extra API calls per webhook event.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Undocumented Reality
&lt;/h2&gt;

&lt;p&gt;Here's what HubSpot won't tell you but you need to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API versions are complex.&lt;/strong&gt; There's v1, v2, and v3 running simultaneously. Some endpoints only exist in v1 (looking at you, Engagements API), some features are v3 only, and they're deprecating v1 "soon" (they've been saying this for three years).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private apps are not the same as OAuth apps.&lt;/strong&gt; Private apps use API keys and are simpler but can't be distributed. OAuth apps can be shared but require the whole token dance. Choose wrong and you'll be rebuilding your integration from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The search API is basically useless.&lt;/strong&gt; It has a different rate limit (4 requests per second), can't search all properties, and sometimes returns stale data. One client had contacts appearing in search results three hours after deletion. The only reliable way to find data is to pull everything and filter locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error messages lie.&lt;/strong&gt; You'll get "Contact already exists" when the real problem is a malformed email. You'll get "Invalid property value" when the property doesn't exist. Always log the full request and response because the error message alone won't help you debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Associations are their own special hell.&lt;/strong&gt; Want to link a contact to a company? That's a separate API call. Want to see all contacts for a company? Another call. Want to update the association? You can't, you have to delete and recreate it. Each operation counts against your rate limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making This Bearable with TypeScript
&lt;/h2&gt;

&lt;p&gt;If you're building anything serious, use TypeScript. HubSpot's API responses are inconsistent and TypeScript will save you from runtime explosions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HubSpotContact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;firstname&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;lastname&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;lifecyclestage&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HubSpotError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;correlationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RATE_LIMIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VALIDATION_ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HubSpotClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HubSpotContact&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HubSpotContact&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`https://api.hubapi.com/crm/v3/objects/contacts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HubSpotError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HubSpot API Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Apideck Escape Hatch
&lt;/h2&gt;

&lt;p&gt;Look, I've built enough HubSpot integrations to know when to quit. If you need HubSpot plus other CRMs (Salesforce, Pipedrive, etc.), stop building separate integrations and use a unified API.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/6zWKVtZxa2nCQrlDdkmscb/3d023f77a4d845ba5c1c345feaf6cf99/Screenshot_2025-09-12_at_23.40.16_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/6zWKVtZxa2nCQrlDdkmscb/3d023f77a4d845ba5c1c345feaf6cf99/Screenshot_2025-09-12_at_23.40.16_2x.png" alt="Screenshot 2025-09-12 at 23.40.16@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apideck's unified CRM API&lt;/strong&gt; handles HubSpot's quirks so you don't have to. One integration instead of five, OAuth complexity handled, rate limiting managed across their infrastructure, and lifecycle stage nonsense abstracted away. Your 6-week HubSpot integration can be done easily within two weeks of actual coding. And not only this, you can configure other CRMs as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apideck's CRM Unified API
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Direct HubSpot API: OAuth dance, token refresh, rate limiting, lifecycle stage hell&lt;/span&gt;
&lt;span class="c1"&gt;// Apideck: Just this&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apideck/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIDECK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIDECK_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consumerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Your customer's ID&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a contact in HubSpot (or any CRM they've connected)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createContact&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contactsAdd&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hubspot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Or 'salesforce', 'pipedrive', etc.&lt;/span&gt;
      &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;phoneNumbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+1-555-1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="c1"&gt;// Lifecycle stage just works - no backwards movement BS&lt;/span&gt;
        &lt;span class="na"&gt;lifecycleStage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lead&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;customFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;industry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Technology&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Actual useful error messages&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Get all contacts with pagination handled automatically&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllContacts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contactsAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hubspot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// No manual pagination, no rate limit management&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The same code works for ANY CRM - just change serviceId&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncToMultipleCRMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hubspot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;salesforce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pipedrive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;crms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contactsAdd&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contactData&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// That's it. No OAuth per platform, no different APIs, no rate limit juggling&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that to the 200 lines of OAuth handling, rate limit retry logic, and lifecycle stage workarounds you need for direct HubSpot integration. One API, consistent data models, errors that actually make sense.&lt;/p&gt;

&lt;p&gt;Tangible benefits that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.apideck.com/blog/what-is-a-unified-api" rel="noopener noreferrer"&gt;Unified API for all CRM platforms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;One integration for HubSpot, Salesforce, Pipedrive, and 50+ others&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.apideck.com/guides/field-mapping" rel="noopener noreferrer"&gt;Field mapping that actually works&lt;/a&gt; - Including HubSpot's lifecycle stages and custom properties&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.apideck.com/apis/crm/hubspot" rel="noopener noreferrer"&gt;No more OAuth gymnastics&lt;/a&gt; - They handle token refresh, expiration, all of it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ship Something That Works
&lt;/h2&gt;

&lt;p&gt;HubSpot's API is powerful but exhausting. You'll spend more time handling edge cases than building features. Every integration starts simple and ends with dozens of workarounds for HubSpot's peculiarities.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2b0biffxoTWxo6QImU0bQ6/f3585a870c84f4a775b267eff76e1ed4/Screenshot_2025-09-12_at_23.41.19_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2b0biffxoTWxo6QImU0bQ6/f3585a870c84f4a775b267eff76e1ed4/Screenshot_2025-09-12_at_23.41.19_2x.png" alt="Screenshot 2025-09-12 at 23.41.19@2x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My advice? Start with the smallest possible integration. Get OAuth working. Make one API call successfully. Handle rate limits properly. Only then add more complexity. And when you inevitably hit the wall where you're spending more time fighting HubSpot than building your product, consider whether a unified API makes more sense.&lt;/p&gt;

&lt;p&gt;The perfect HubSpot integration doesn't exist. Ship something that works, iterate based on which errors your customers actually hit, and keep a bottle of whiskey handy for when HubSpot changes something without warning.&lt;/p&gt;

&lt;p&gt;Because they will, they always do.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
