Skip to content

FarmLoop Processing API

The Processing API lets your own software do what the FarmLoop web app does: send a sliced file plus your settings to our server and receive the FarmLoop-modified file back, ready to print with the detachment sequences injected. Your farm management software, a watch folder script or your own tools can run FarmLoop without anyone opening the app.

  1. A FarmLoop Business plan on the account you use at app.3d-farmers.com.
  2. An API key (fl_…). Create one in the app under API Keys. Treat it like a password: it acts as your account. If it leaks, revoke it on the same page and create a new one.
POST https://api.3d-farmers.com/api/v1/process

Authentication is a single header on every request:

X-API-Key: fl_your_key_here

A quick way to check your key works before sending files:

Terminal window
curl -sS https://api.3d-farmers.com/api/v1/process \
-H "X-API-Key: fl_your_key_here"

GET on the same URL returns a machine-readable description of the API. If you get JSON back (not a 401/403), you’re in.

Send the sliced file as the multipart field file. To combine several plates into one print job, send up to 10 files as the field files instead: each is processed with the same settings and they are merged into one file, like Download All in the app. All files must be sliced for the same printer model.

  • Accepted: .gcode.3mf, .3mf (must be sliced, i.e. contain gcode), .gcode
  • Not accepted: binary G-code (.bgcode), project files without gcode
  • Max size: 200 MB per request

All settings are optional multipart fields:

FieldValuesDefault
presetname of a saved preset (see below)none
detachmentModepush / liftpush
stage1 / 21
cooldownTypetemperature / timetemperature
cooldownTemperature°C, 20-10030
cooldownTimeseconds, 0-86400300
heatedBedTemperature°C, 0-120taken from your slicer file
zOffsetmm, -5 to 5taken from your slicer file
pushHeightmm, 0-3005
useFanUpgradetrue / falsefalse
copies1-991
copiesPerFileone count per file, e.g. 2,1,3copies for every file
naturalCooldownMinutes0-1200 (off)
triggerCalibrationMm0-30 (off)
sweepRoutineJSON, see belowthe standard sweep

copies repeats the print inside one gcode file with the ejection sequence between loops, so the printer runs all copies unattended in a single job. With several files, copiesPerFile sets the count per file in upload order. Output size grows with each copy; requests whose expanded output would exceed 400 MB are rejected with 413.

The three beta tools from the app’s Detachment Mode and Cooldown Settings are available over the API too:

  • naturalCooldownMinutes holds the part in the closed chamber with the fans off for this many minutes before the Stage 2 door opens. Stage 2, enclosed printers.
  • triggerCalibrationMm presses the Stage 2 kit switch this many mm deeper, for kits that do not trigger reliably. Stage 2.
  • sweepRoutine replaces the fixed push with your own routine: a list of pushes (each with its own height, speed and lanes) followed by up to five clearing passes. clearings can be an empty list: the routine then ends after the pushes, and anything they miss stays on the plate. Push mode only. Between stages the head always goes up and back to its safe position, so a lower pass never runs into a part.
{
"pushes": [
{ "xPositions": [], "speed": "slow", "heightMm": 60 },
{ "xPositions": [128, 128], "speed": "medium", "heightMm": 20 }
],
"clearings": [{ "xPositions": [], "speed": "fast" }]
}

xPositions lists the lanes in mm; an empty list means the whole plate, and repeating a lane repeats the pass. speed is slow, medium or fast. heightMm is optional and falls back to pushHeight.

Settings the chosen stage or mode ignores (for example trigger calibration on Stage 1) are reported in X-FarmLoop-Warning, and the file is still processed.

A preset is a named set of settings. Save it once, then send only the file and preset=<name>; any field you send as well overrides the preset for that request. Changing a preset changes every farm script that uses it.

The quickest way to make one: tune a file in the app, then choose Save as API preset in the Ready to Process panel. The app saves the choices on screen (mode, stage, cooldown, fan upgrade, beta tools). Bed temperature and Z offset are read from each file, and push height is saved only if you set it yourself.

Your presets are listed on the API Keys page. Over the API:

Terminal window
# list
curl -sS https://api.3d-farmers.com/api/v1/presets -H "X-API-Key: $FARMLOOP_API_KEY"
# create or replace
curl -sS -X PUT https://api.3d-farmers.com/api/v1/presets/p1s-petg-stage2 \
-H "X-API-Key: $FARMLOOP_API_KEY" -H "Content-Type: application/json" \
-d '{"description": "PETG on the P1S row", "params": {"stage": 2, "cooldownTemperature": 28, "naturalCooldownMinutes": 5}}'
# delete
curl -sS -X DELETE https://api.3d-farmers.com/api/v1/presets/p1s-petg-stage2 -H "X-API-Key: $FARMLOOP_API_KEY"

Preset names are 1-50 letters, numbers, spaces, dots, dashes and underscores, and are matched without regard to case. You can keep up to 50.

Terminal window
# one file, with a preset
curl -sS https://api.3d-farmers.com/api/v1/process \
-H "X-API-Key: fl_your_key_here" \
-F "file=@benchy.gcode.3mf" \
-F "preset=p1s-petg-stage2" \
-OJ
# three plates merged into one job, 2 + 1 + 3 copies
curl -sS https://api.3d-farmers.com/api/v1/process \
-H "X-API-Key: fl_your_key_here" \
-F "files=@plate1.gcode.3mf" -F "files=@plate2.gcode.3mf" -F "files=@plate3.gcode.3mf" \
-F "copiesPerFile=2,1,3" -F "stage=2" \
-OJ

-OJ saves the response using the server-suggested filename.

import requests
from urllib.parse import unquote
resp = requests.post(
"https://api.3d-farmers.com/api/v1/process",
headers={"X-API-Key": "fl_your_key_here"},
files={"file": open("benchy.gcode.3mf", "rb")},
data={"preset": "p1s-petg-stage2"},
timeout=300,
)
resp.raise_for_status()
if resp.headers.get("X-FarmLoop-Warning"):
print("Warning:", resp.headers["X-FarmLoop-Warning"])
out_name = unquote(resp.headers["X-FarmLoop-Output-Name"])
with open(out_name, "wb") as f:
f.write(resp.content)
print("Saved", out_name, "| printer:", resp.headers["X-FarmLoop-Printer"])
const fs = require("node:fs");
const form = new FormData();
form.append("file", new Blob([fs.readFileSync("benchy.gcode.3mf")]), "benchy.gcode.3mf");
form.append("preset", "p1s-petg-stage2");
const resp = await fetch("https://api.3d-farmers.com/api/v1/process", {
method: "POST",
headers: { "X-API-Key": "fl_your_key_here" },
body: form,
});
if (!resp.ok) throw new Error(await resp.text());
const outName = decodeURIComponent(resp.headers.get("x-farmloop-output-name"));
fs.writeFileSync(outName, Buffer.from(await resp.arrayBuffer()));

On success (200) the body is the modified file, plus these headers:

HeaderMeaning
X-FarmLoop-PrinterDetected printer model (e.g. P1S, X1E, H2D)
X-FarmLoop-StageStage that was processed (1 or 2)
X-FarmLoop-FilesNumber of files in the job
X-FarmLoop-CopiesTotal copies baked into the returned file
X-FarmLoop-PresetThe preset used, if any
X-FarmLoop-Output-NameSuggested filename, URL-encoded
X-FarmLoop-WarningOnly present when something needs your attention - see below

Processing still succeeds, but X-FarmLoop-Warning is set when:

  • the file was sliced with OrcaSlicer (not supported - re-slice with Bambu Studio v02.04+),
  • the file carries an outdated Bambu Studio printer profile (pre-v02.04 - update Bambu Studio and re-slice with the stock profile),
  • the filename suggests the file was already processed by FarmLoop (re-processing duplicates the injected sequences), or
  • a setting does not apply to this stage, mode or printer and was ignored.

Surface these warnings to your users - a warned file may print without the detachment sequence working.

StatusMeaning
400Invalid settings or unsupported file type - the message names each problem
401Missing/invalid API key
403No Business plan on the account, or no active subscription
404Unknown preset name
413File over 200 MB, or expanded output (files × copies) over 400 MB
422Unreadable 3MF, no gcode inside, unknown printer type, model too tall for Stage 1, or files for different printers
429Rate limit: max 10 requests per 5 minutes per key. Retry-After header tells you when to retry
503Server at capacity (max 2 concurrent jobs). Retry after a few seconds

Recommended client behavior: honor Retry-After on 429/503, and treat 4xx responses as permanent for that file/request (don’t blind-retry). A merged request counts once against the rate limit, so batching plates with files is the efficient way to send many.

If you’re writing your integration with an AI coding assistant (Claude, Cursor, Copilot, ChatGPT, …), we maintain a machine-readable spec written specifically for agents - the complete API contract, validation ranges, required retry behavior, and reference implementations, with no marketing prose to wade through:

processing-api-for-ai-agents.md

How to use it:

  • Claude Code / CLI agents - save it into your project (e.g. docs/farmloop-api.md) and reference it, or paste the URL into the chat:

    Terminal window
    curl -sSo docs/farmloop-api.md \
    https://help.3d-farmers.com/integrations/processing-api-for-ai-agents.md
  • Chat assistants - paste the URL (or the file’s contents) into the conversation and ask the assistant to build against it.

The file instructs the agent to read your API key from the FARMLOOP_API_KEY environment variable - set that before running generated code, and never let the key be committed or logged.

API questions, key rotation, or limit increases: contact FarmLoop support and mention “Processing API”.