Spreadsheets

Change a spreadsheet's structure

Inserts and deletes rows, columns and sheets, and changes merges, sorting, charts and validation. Requires sheets.structure.write, which is separate from writing values.

POST /v1/spreadsheets/:id/batch-update

Permission

The key must be granted sheets.structure.write

A key without it gets 403 insufficient_scope, with the missing permission named in details.

Needs an If-Match header carrying the state from /state, and answers 422 without one. These are the changes that move data other requests address by position — inserting a row shifts every reference below it — so one applied against a stale view merges cleanly into a corrupt grid. A 409 means the document changed since your read: read state again and reapply. Retrying is safe on its own, because a successful apply moves the state and a replay answers 409 rather than applying twice.

Path parameters

Field Type Description
id required string The spreadsheet to restructure.

Headers

Field Type Description
If-Match required string The state token from the spreadsheet's /state, which is what makes this change conditional on the document you actually read. Without it the request is refused rather than applied to whatever the document has become.

Request body

Requests apply in order and each sees the effect of the one before, so adding a sheet and writing to it in the same batch works.

Field Type Description
requests required object[] The changes to apply, in order. Each entry names exactly one operation. At least one. Its fields are below.

Each item in requests

Field Type Description
add_sheet object Add a tab. Its reply carries the new sheet_id. Its fields are below.
delete_sheet object Remove a tab and everything on it. The last remaining sheet cannot be deleted. Its fields are below.
update_sheet_properties object Rename, move, recolour or hide a tab. Its fields are below.
merge_cells object Merge a rectangle, dropping any merge it overlaps. Its reply carries merge_id. Its fields are below.
unmerge_cells object Split the merges in a range. Its reply carries how many were removed. Its fields are below.
update_dimension_properties object Resize or hide rows and columns. Its fields are below.
repeat_cell object Apply formatting across a range. Its fields are below.
sort_range object Sort a range by one of its columns. Its fields are below.

The add_sheet

Field Type Description
title string Defaults to the next free SheetN.
index integer Where among the tabs. Appends when omitted.

The delete_sheet

Field Type Description
sheet_id required string

The update_sheet_properties

Field Type Description
sheet_id required string
properties required object What to change. Omitted properties are left alone. Its fields are below.

The properties

Field Type Description
name string
index integer
color string Hex, for example #cc0000. An empty string clears it.
hidden boolean

The merge_cells

Field Type Description
range required A1 range

The unmerge_cells

Field Type Description
range required A1 range

The update_dimension_properties

Field Type Description
sheet_id required string
dimension required string ROWSCOLUMNS
index required integer Which row or column, from 0.
pixel_size integer null restores the default size.
hidden boolean

The repeat_cell

Field Type Description
range required A1 range
style object Merged with the formatting already on each cell, so applying bold does not wipe a fill. A null value clears one attribute. Its fields are below.
number_format string A format pattern, for example $#,##0.00.

The style

Field Type Description
b 0 or 1 Bold.
i 0 or 1 Italic.
s 0 or 1 Strikethrough.
u 0 or 1 Underline.
fg string Text colour, hex.
bg string Fill colour, hex.
ha string leftcenterright
va string topmiddlebottom

The sort_range

Field Type Description
range required A1 range
column required integer Absolute column index, not an offset within the range.
descending boolean Defaults to false

Request

curl https://api.veruapis.com/v1/spreadsheets/:id/batch-update \
  -X POST \
  -H "Authorization: Bearer vak_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "requests": [
    { "add_sheet": { "title": "Q4", "index": 1 } },
    { "repeat_cell": { "range": "Q4!A1:D1", "style": { "b": 1, "bg": "#eeeeee" } } }
  ]
}'

Idempotency-Key is required on every write. Retrying with the same key returns the original response rather than repeating the operation, so a timeout is something you retry rather than something you investigate.

Response

Field Type Description
replies object[] One entry per request, in the same order, carrying whatever that operation returns: sheet_id for add_sheet, merge_id for merge_cells, removed for unmerge_cells. An empty object where an operation returns nothing.
state string The document's state after the write. Send it as If-Match on a structural change, or compare it with your last one to notice somebody else's edit.
changed boolean False when the write stored nothing new — writing a cell the value it already holds. Not an error and not worth retrying: no new state was produced and no collaborator was notified.
{
  "data": {
  "replies": [
    { "add_sheet": { "sheet_id": "s0199a1c2...", "title": "Q4" } },
    {}
  ],
  "state": "d4e5f6a7b8c9d0e1",
  "changed": true
},
  "meta": { "request_id": "req_01j8z9...", "timestamp": "2026-09-02T10:00:00Z" }
}
Worth knowing

Structural changes only, and they need If-Match carrying the state you read: inserting a row moves everything below it, so applying one against a stale view leaves a corrupt grid. Requests apply in order and each sees the one before, so adding a sheet and formatting it in one batch works. replies is positional, with {} where a request has nothing to return.

Errors

Every failure uses the same shape, with a stable string code you can branch on. See the error reference.

Related

POST /v1/spreadsheets/:id/batch-update

id
Sends this body to your own workspace, for real. Edit it before running.