Everything runs inside Google Sheets. After adding one Apps Script file and an
API key, you can extract tables with a spreadsheet menu command.
Why Extract Tables in the Sheet
A table might arrive as a screenshot, a photo, or an image embedded in a report. Retyping it is slow and introduces errors. In this guide, we’ll add a menu command that extracts the table from a selected image and writes it to a second sheet. The script uses/parse to identify the
document structure, including tables. It doesn’t need a predefined schema for
each table layout.
What We’ll Build
A spreadsheet-bound Apps Script that:- Reads the image out of the selected cell.
- Sends it to
/parseand waits for the job to finish. - Finds the first table in the result and turns its HTML into rows.
- Writes those rows to a new results sheet.
Show the Full Script
Show the Full Script
Paste this into
Code.gs, replacing its contents. The script reads the API key
from a script property configured in Step 3.Code.gs
Requirements for Extracting Tables in Google Sheets
Before you start, gather:- A Google account and a spreadsheet you can edit.
- An Unsiloed API key from the dashboard.
- A PNG, JPEG, or TIFF image containing a table. This guide uses a fund performance page so you can follow along with the same numbers.
Step 1: Put the Image in a Cell
Google Sheets can hold an image two ways. Insert image in cell makes the image a cell value. Insert image over cells leaves it floating above the grid. Pasting with Ctrl+V creates the floating kind. The script can read only an in-cell image. TheOverGridImage class used for
floating images doesn’t expose the image bytes.
1
Insert the image into the cell
Select the cell you want the image in, then choose Insert → Image → Insert
image in cell.
Paste the image URL, or upload a file, and click Insert.

2
Check that it really is in the cell
The image should sit within the cell borders and resize with the row and
column.
If it floats above the grid, open its three-dot menu and choose Put image in
cell.

Step 2: Write the Script
We’ll add the script in sections. If you copied the completed version above, use this section as a reference.2.1 Open the Script Editor
From the spreadsheet, choose Extensions → Apps Script. This creates a script bound to this spreadsheet and opens it in a new tab.
Code.gs with an empty myFunction. Delete it, then add each
code block below.

2.2 Configuration and the Menu
InCode.gs, add the API configuration and spreadsheet menu:
Code.gs
onOpen function when the spreadsheet opens, which
adds the Unsiloed menu. The unsiloedApiKey helper reads the current key from
a script property and reports a clear error if it is missing. Step
3 configures that property.
2.3 Read the Image Out of the Cell
AddimageBlob below onOpen. The getValue() method returns a CellImage, so
the function fetches its bytes from a Google-hosted URL:
Code.gs
setName call. The /parse endpoint chooses a decoder from the file
extension, so an unnamed blob is rejected. The MIME-type lookup accepts the three
supported image formats and gives the blob a matching filename.
2.4 Send the Image to Parse
AddparseImage below imageBlob. Because /parse is asynchronous, the function
submits the image and polls the job endpoint until parsing succeeds or fails:
Code.gs
UrlFetchApp service builds a multipart request from the blob in payload
and uses the blob name as its filename.
2.5 Turn the Table Into Rows
AddtableRows below parseImage. It finds the first Table segment and converts
its html field into rows:
Code.gs
- The parser validates that the result contains rows and cells, preserves line breaks, and decodes common named and numeric HTML entities.
- Formula-leading text gets an apostrophe prefix before
setValues, preventing extracted document content from executing as a spreadsheet formula. - Spanning cells can make some rows shorter. Because
setValuesrejects uneven rows, the function pads missing cells at the end. It doesn’t reproduce complexrowspanor middle-columncolspanlayouts.
find takes the first table on the page. See
Extend the Google Sheets Integration for
handling several.
2.6 Write the Rows to the Sheet
AddextractTable below tableRows. This menu handler validates the selection,
runs the helpers, and writes the result:
Code.gs
valueType check accepts only in-cell images. Each run creates a new sheet,
using a timestamp in the name when Extracted already exists, so it never clears
an earlier result. A single setValues call writes the full table without making
a separate Sheets request for every cell.
Save Code.gs.
Step 3: Store Your API Key
Store the API key outside the source code so it isn’t included when you share or copy the script.1
Add the property
In the Apps Script editor, open Project Settings from the left sidebar and
scroll to Script Properties. Click Add script property. If the project
already has properties, click Edit script properties first.Name it 
Click Save script properties.
UNSILOED_API_KEY and paste your key as the value.
Step 4: Authorize the Script
The first run asks for permission to edit the spreadsheet and call the Unsiloed API.1
Run it once and grant access
Back in the spreadsheet, reload the page. A new Unsiloed menu appears in
the menu bar. Select the cell holding your image and choose Unsiloed →
Extract Table From Image.Google Sheets shows Authorization required.
Click OK, then pick your Google account in the window that opens.

2
Get past the unverified app warning
Google warns that it hasn’t verified the app because this is your unpublished
script. Click Advanced, then Go to <your project name> (unsafe).Review the two permissions it asks for and click Allow:
- Spreadsheet access to read the image and write the rows
- External service access to call the Unsiloed API
Step 5: Extract the Table
With the script authorized, the menu command is the whole workflow. Select the cell holding the image and choose Unsiloed → Extract Table From Image.
Sample Output
For the sample fund performance page, theExtracted sheet preserves the group
rows and the em dash for the missing five-year return:

What to Expect From the Output
The rows preserve the table text rather than normalizing it:- Values arrive as text.
7.76%keeps its percent sign, and an em dash remains an em dash. Formula-leading values are escaped before writing. Convert the values in Sheets if you need numbers. - Layout rows come through. A grouping row like
Class A Sharesarrives as a label with empty cells beside it, exactly as it sits on the page. - Row grouping can vary. A label and its values might share a row or split across two rows. Don’t write formulas that assume a fixed row offset.
/v2/extract with a schema instead.
This requires a schema but returns typed values in predictable columns.
Troubleshoot the Script
'Select a cell holding an image placed in the cell'
'Select a cell holding an image placed in the cell'
The selected cell has no in-cell image. Either the wrong cell is selected, or
the image is floating over the grid rather than in a cell. See
Step 1.
'No table found in that image'
'No table found in that image'
/parse found no table. Try a larger or sharper image, especially if the
layout has no ruling lines or consistent columns.The job fails with 'Unsupported file type'
The job fails with 'Unsupported file type'
Use a PNG, JPEG, or TIFF image. The script rejects other MIME types before
submission and gives supported blobs a matching file extension.
'Exception: Request failed ... returned code 401'
'Exception: Request failed ... returned code 401'
The API key is missing or wrong. Check the
UNSILOED_API_KEY script property,
then run the command again.Extend the Google Sheets Integration
The script takes the first table it finds. To pull every table out of a multi-table page, collect all theTable segments instead of calling find, and
write each one below the last.
To process many images, place one image per row, loop over the range, and write
each result to its own sheet. Keep enough margin below the six-minute Apps Script
limit for slower jobs and retries. For larger batches, submit every job first,
then poll them together so they run concurrently.
Parsing
What
/parse returns for a document, segment by segment.Element Types
The full list of segment types, including
Table.Extraction
Pull typed fields against a schema when you need numbers rather than text.
API Reference
The full request and response specs for
/parse.
