Sheets
Convert Google Sheets, XLSX or CSV into a credited XLSX workbook.
Convert links throughout a Google Sheet or uploaded workbook using your account's bearer token. Output is always .xlsx, including for CSV input. Links are replaced in place, with your account's credit and optional campaign and label as described in Links.
Google Sheet input
Submit a Google Sheets URL that NinjaBuy can read. Conversion reads every tab, not just the display preview, and freezes a snapshot for this job.
curl -sS -D sheet-headers.txt https://www.ninjabuy.com/api/v1/sheets \
-H "Authorization: Bearer $NINJABUY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{"url":"https://docs.google.com/spreadsheets/d/SHEET_ID/edit","campaign":"spring-haul","label":"finds"}'XLSX or CSV input
First reserve an upload. filename must end in .xlsx or .csv and contain 1 to 255 characters after trimming. bytes is the exact file size in bytes, from 1 to 20,971,520.
curl -sS https://www.ninjabuy.com/api/v1/uploads \
-H "Authorization: Bearer $NINJABUY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{"filename":"finds.xlsx","bytes":12345}'The 201 response contains upload_id, a signed PUT url, expires_at (Unix epoch milliseconds), max_bytes and request_id. The reservation expires after 15 minutes. PUT the raw file to the returned URL, then submit its upload id:
curl --fail-with-body -X PUT "$UPLOAD_URL" \
--data-binary @finds.xlsx
curl -sS -D sheet-headers.txt https://www.ninjabuy.com/api/v1/sheets \
-H "Authorization: Bearer $NINJABUY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{"upload_id":"UPLOAD_ID","campaign":"spring-haul","label":"finds"}'The direct PUT uses the storage service's response contract. NinjaBuy validates the uploaded bytes when you submit the conversion. Unknown or other accounts' upload ids return 404. The stored object must match the declared byte count; an object larger than declared or the maximum returns 413 upload-too-large, and a smaller (truncated) object returns 422 invalid-workbook with the byte-count mismatch explanation. These checks happen before downloading and also apply if the bytes change after the metadata check. Refused raw uploads are deleted, and accepted raw uploads are deleted once their validated snapshot is recorded.
Send exactly one source: url or upload_id. Both accept optional campaign and label; no async field is needed. Both POST routes accept Idempotency-Key and each consumes one slot of the shared 30-per-minute create window. Child link conversions consume no extra slots.
Job and download
A sheet submission returns 202 with Location: /api/v1/jobs/JOB_ID, Retry-After: 5 and this body:
{
"job": {
"id": "JOB_ID",
"kind": "sheet_conversion",
"status": "queued",
"poll_after_seconds": 5
},
"request_id": "REQUEST_ID"
}Poll Jobs with the same account's token until done or failed, respecting poll_after_seconds. A completed job.result has kind: "sheet_conversion", counts and a signed download_url valid for 15 minutes. Poll again for a fresh download signature. A failed job has error and no partial download.
For a Google Sheet, job.result may also include copy_url, a view-only link to an exact Google Sheets copy with your links in place; anyone with the link can keep their own with File → Make a copy. copy_notes lists what a reader of that copy should know, such as pictures made with =IMAGE() needing one Allow access click in their copy. download_url still returns the XLSX. copy_counts gives the same counts for the copy; when the XLSX cannot be made, the result keeps copy_url and has no download_url, and copy_notes says why. download_notes lists, line by line, what the XLSX cannot hold of the sheet, such as charts or a second link in a cell.
Snapshots and result files are retained for seven days after the job finishes or fails. After cleanup, the job remains readable with its status and counts, and job.result.download_url is omitted. Shared snapshots stay while another job still needs them. Abandoned raw uploads are deleted one hour after the upload reservation expires; a validated upload that never starts a job loses its snapshot seven days after reservation expiry. Cleanup runs hourly.
| Count | Meaning |
|---|---|
links_found | Discovered link occurrences, including repeated targets. |
converted | Occurrences replaced with credited links. |
left_by_host | Counts grouped by host for links intentionally left unchanged. |
unresolved | Occurrences that could not be converted and remain unchanged. |
unresolved_examples | Example unresolved URLs. |
coverage_gap | Missing conversion answers; always 0 in a produced file. |
Coverage satisfies links_found = converted + sum(left_by_host) + unresolved. Social links, Google Sheets links, same-workbook navigation and existing NinjaBuy links stay unchanged. Every conversion candidate must receive an answer before output is written; missing answers fail the job.
Workbook preservation
The output preserves styles, merges, row and column sizes, and pictures as represented by the parsed grid. Rich-text runs survive in cells without a cell hyperlink; linked cells use one text label and hyperlink. It is a newly serialized workbook: charts, macros and other features outside that grid are not preserved.
XLSX supports one hyperlink per cell. The conversion replaces links in place instead of adding a second link beside them. A cell with two or more different non-picture link targets keeps the first as its hyperlink and lists the others in the cell's note, so no target is lost.
Limits
| Resource | Maximum |
|---|---|
| Uploaded XLSX or CSV file | 20 MiB; must match the declared byte count |
| Expanded XLSX or combined Google source HTML | 64 MiB |
| Cells across the conversion | 500,000 |
| Discovered link occurrences, including repeats | 100,000 |
| Each picture | 10 MiB |
| Retained unique pictures combined | 32 MiB |
| Serialized XLSX output | 64 MiB |
| Upload reservation / download signature | 15 minutes each |
Exceeding a parser or writer bound fails explicitly. See Errors for invalid-workbook, upload-expired and upload-too-large.