How to Append Data to an Excel Worksheet via API Without Opening Excel
Every business has at least one spreadsheet that quietly runs half of it. A "DailySales" workbook that finance refreshes every morning. A response log that catches every form submission before anyone builds a real database for it. A CRM export that gets emailed around every Monday because nobody trusts the dashboard. These workbooks work, right up until someone has to update them without actually opening Excel: a scheduled job, a webhook, a background service that needs to write five new rows into a worksheet that a human is going to open two hours later.
That's a smaller problem than it sounds, and also a bigger one. Smaller, because appending rows to an Excel file is conceptually trivial: take some data, put it below the last row. Bigger, because the moment you actually write that code yourself, you run into questions a one-line "append a row" ask never mentions. Does the new data need to match an existing header row, or go into specific cells? What happens if the sheet is a real Excel Table, with named columns and formatting that breaks if you write past its boundary? Does a date string need to become an actual Excel date, or does it arrive as plain text? And what happens when the thing that needs those three new rows is a serverless function that has no business knowing what openpyxl or ClosedXML is?
PDF4me's Excel Add Rows API answers this with a single HTTP call that takes a workbook and a JSON payload and hands back the updated workbook. No Excel installation, no COM interop, no spreadsheet library to patch every time Microsoft changes something. It's worth walking through exactly how it works, because the two insertion modes it supports, table mode and coordinate mode, are where most hand-rolled "just append a row" scripts eventually get it wrong.
What the Add Rows Endpoint Actually Does
The REST endpoint is POST office/ApiV2Excel/ExcelAddRows. Headers: Content-Type: application/json and Authorization: Basic YOUR_BASE64_ENCODED_API_KEY (get a key at the PDF4me dashboard). You send a document object holding the workbook's filename, a docContent field with the workbook's Base64-encoded bytes, and an addRowsToExcelAction object that carries the actual instructions. The one field every call needs inside that action object is jsonInput: a JSON array of objects (or a single object) given as a string, where each object becomes one new row.
That last detail trips people up the first time. jsonInput isn't a nested JSON array in your request body; it's a string containing JSON, which means the quotes inside it have to be escaped.
Sample request:
{
"document": { "Name": "data.xlsx" },
"docContent": "UEsDBBQABgAIAAAA...",
"addRowsToExcelAction": {
"jsonInput": "[{\"Name\":\"John\",\"Age\":30}]"
}
}
The API returns the updated Excel document as a Base64-encoded string in JSON, alongside fileName, success, and errorMessage fields.
Table Mode vs. Coordinate Mode
This is the part a DIY script usually gets wrong, because it's invisible until it isn't. The API supports two different ways of deciding where new rows land, and mixing their parameters in the same call returns an error rather than a guess.
Table mode is for worksheets where the data already lives inside a real Excel Table object. Set tableName to that table's name, and the API matches each JSON object's property names to the table's column headers, then inserts the row at the position given by excelRowNumber (1-based). Matching happens by header name, not column order, so the order of keys in your JSON doesn't need to match the order of columns in the sheet.
Coordinate mode is for a plain range of cells with no formal table object. Leave tableName empty, and set insertFromRow and insertFromColumn (both 1-based) to the starting cell. Values are written in the order the JSON properties appear, with no header matching at all, which also means a coordinate-mode write can silently overwrite existing cells if the start position is wrong. There's no undo button on an API call, so get the starting cell right first.
A few more fields round out the behavior: convertNumericAndDate (default true) turns a string like "2026-10-08" into a real Excel date using dateFormat (yyyy-MM-dd default) and numericFormat (N2 default), with cultureName (en-US default) controlling how ambiguous dates and decimals get parsed. ignoreNullValues skips writing a cell at all when a property's value is null. ignoreAttributeTitles makes table-mode header matching case-insensitive, which is the difference between a silent miss and a working match when your source sends CustomerName and your table header is customername. worksheetName defaults to Sheet1 if omitted.
Why This Beats Writing It Yourself
A hand-rolled version in Python or C# isn't hard to start. It's hard to finish. You end up handling the table-vs-coordinate branching yourself, writing your own type-conversion logic for dates and numbers across locales, and testing against Excel quirks (shared formulas, conditional formatting that doesn't extend, a table that refuses to grow past its defined bounds) that only show up once real users send real data. A REST call with a JSON body and a Base64-encoded file is something almost any environment, including a serverless function with no business installing a spreadsheet library, can make without new dependencies.
Using It Without Writing Code: Power Automate, Zapier, n8n, and Make
The same action is available as a native step in the no-code automation platforms PDF4me integrates with.
In Power Automate, the Excel - Add Rows action takes the same fields as the REST call and returns the updated workbook plus Success, ErrorMessage, and Errors a flow can branch on. Documented scenarios: a scheduled SQL query feeding a "DailySales" table every morning, a Microsoft Forms submission landing in the next open row of a response log saved back to SharePoint, and a weekly CRM export populated into an "Opportunities" table with case-insensitive header matching.
Zapier's Add Rows to Excel action follows the same shape, with a JSON array from an earlier Zap step mapped into the action's JSON Input field. Its own documented example, a daily sales export written into a templated "DailySales" table on Google Drive with date/currency formatting applied before upload and email, claims it saves roughly two hours of manual entry a day. Worth treating as Zapier's own stated estimate, not an independently verified number.
n8n's PDF4me Excel: Add Rows node surfaces a detail the others don't: its output includes rowsAdded, a count you can compare against your input array's length with an IF node to verify every row actually landed, rather than assuming success because the call didn't error. The same action is also available through Make, PDF4me's fourth supported no-code platform, via its own app listing on Make's marketplace.
Where This Fits and Where It Doesn't
This API appends rows to an existing workbook; it doesn't build one from scratch. For generating a brand-new Excel file from JSON data, see Convert JSON to Excel. For removing rows afterward, there's a matching Delete Rows endpoint built the same way.
The honest limit: this is a synchronous, stateless call. It doesn't watch a folder, doesn't know your data source, and doesn't decide when to run; something else (a cron job, a webhook, a Power Automate trigger) still has to supply the workbook, the JSON, and the timing. What it removes is the part that used to require either a human with Excel open or a maintained library of spreadsheet-manipulation code.
Sample code and SDKs: pdf4me-api-samples on GitHub
Website: pdf4me.com
Documentation: docs.pdf4me.com
Top comments (0)