Shipping scripts
Updated · SMART Shipping Rates & Rules (E-TRADE PARTNER)
A shipping script is your own JavaScript that runs at checkout after your zones and combined rates. It reads the cart, addresses, customer, totals, and the rates your zones made, and returns the final list of up to 20 rates. Scripts live in Shipping rates > Scripts on the Pro plan. They run in a sealed sandbox with no internet access and a 5 ms CPU limit. If a script fails, that one checkout gets your zone rates or a backup rate instead.
SMART Shipping Rates does this with zones, calculated rates, and delivery customizations.
Try it freeOn this page
Use a script when the zone editor can't express your rule: a fuel surcharge on every rate, freight priced by a metafield, a fee per extra vendor, a price table by postcode, or delivery dates that skip holidays. For the usual weight, cart total, quantity, and postcode rates, zones are simpler. Moving from Shopify's retired Ruby scripts? Read what replaces Shopify shipping scripts.
Where a script runs
Shopify asks the app for rates -> zones and their rate conditions -> Settings: multiple rates, rounding -> combined rates -> your active script -> Settings again on the script's answer -> rates at checkout
- A script sees and returns the rates this app sends to checkout. It does not see Shopify's own rates or other apps' rates. To hide or rename those, use Delivery customizations.
- With no active script, checkout gets exactly what the zones and combined rates made.
- The script still runs when no zone matched, with an empty
input.rates, so it can supply rates on its own. - One script can be active per store. Activating one turns the others off. A store can keep up to 10 scripts.
- It runs on the app's servers as part of the carrier-service answer, not as a Shopify Function, so it can set prices and add rates. That also means it needs Carrier Calculated Shipping, like any calculated rate.
Write a script
The code is the body of a function. input is already defined, and the code ends with return. Return a list of rates, or input.rates to keep your zone rates. Returning nothing (undefined or null) also keeps input.rates.
// Freight above 30 kg, otherwise keep the zone rates.
const kg = input.items.reduce((s, i) => s + i.grams * i.quantity, 0) / 1000;
if (kg > 30) return [{ name: "Freight", price: 49 }];
return input.rates;// Add a 9% fuel surcharge to every rate.
return input.rates.map((rate) => ({
...rate,
price: rate.price * 1.09,
}));The code must stay one function body. The editor checks this on save and on every Test, without running your code, and rejects code that closes the function early.
What input contains
All money is in the store currency as a decimal (25.5 means 25.50). Input reference in the editor shows the same list.
| Field | What it holds |
|---|---|
| input.items | One entry per cart line: name, sku, quantity, grams, vendor, price (unit price), requires_shipping, properties (line item properties as name: value), product_id, variant_id |
| item.product | Only what you tick in Data to load: tags, collections (ids as strings), type, metafields such as "custom.floor_metres" |
| item.variant | metafields you ticked, and dimensions (length, width, height, unit; longest side first) or null |
| input.destination | country, province, city, postal_code, address1, address2, address3, company_name, phone, latitude, longitude (latitude and longitude are often empty) |
| input.origin | The ship-from location: country, province, city, postal_code, address1, address2, name |
| input.customer | id (null for guests) and tags |
| input.totals | subtotal, total, discount |
| input.currency, input.locale | Checkout currency and language |
| input.shop | currency, weight_unit, dimension_unit, timezone |
| input.now | date, time, weekday (Monday is 1) in the store timezone, plus utc_time |
| input.rates | The rates your zones and combined rates made: name, price, code, description, min_days, max_days, phone_required, tags |
What to return
| Field | Rule |
|---|---|
| name | Required. 1 to 255 characters. Shown at checkout. |
| price | Required. A number from 0 to 1,000,000 in the store currency. |
| code | Optional. Defaults to the name. Codes may repeat. |
| description | Optional. Up to 255 characters, shown under the name. |
| min_days, max_days | Optional. Whole business days, 0 to 365. |
| phone_required | Optional. Asks the customer for a phone number. |
Return at most 20 rates. A rate you pass back unchanged from input.rates reaches checkout exactly as the zones made it, so return input.rates gives the same checkout as no script. Rounding from Settings > Rate behavior applies only to rates the script added or changed. The Multiple rates setting (all, highest, lowest, or cheapest and most expensive) applies to the whole list the script returns.
Data to load
Tick only what your code reads, because each ticked item is looked up in Shopify and cached: Product tags, Collections, Product type, Product dimensions, Product metafields, and Variant metafields (up to 10 of each, written as namespace.key). Tags, collections, and dimensions share the app's product data cache with zones (Settings > Product data cache has a Clear cache button). Product type and metafields are cached for up to an hour, so a change can take that long to reach checkout.
Templates
A new script starts with Start from my current rates (returns input.rates, so checkout doesn't change until you edit) or Pick a template. Templates in the page header opens the same list. Picking a template ticks the data it needs.
- Keep my current rates
- Fuel surcharge on every rate
- Free shipping above an amount, except tagged products
- Freight by floor metres (a variant metafield, or weight when higher)
- Most expensive item plus a percentage per extra item
- Fee per extra vendor
- Price every item by its own weight band
- Free shipping per product group
- Stackable items
- Fallback weight for weightless items
- Delivery dates with a cutoff and holidays
- Price table by postcode
- Same product has a tag and is in a collection
Test, save, and versions
- Open Shipping rates > Scripts and click Add script.
- Enter a Script name (only you see it; rate names come from your code).
- Write or pick the code in the Code card.
- Click Test, add products and quantities, set the destination and Ship from location, and click Run test. The result shows the input, the zone rates, your output, and the rates checkout would get.
- Set Status to Active and save. Every save is a version.
- Check real requests later in Shipping rates > Activity: each request shows a Script card with the result, and rates the script dropped are marked "Removed by your script".
Test runs and save checks are limited to 10 a minute per store. Checkout runs are not limited. The Versions card keeps the last 50 saves; open one to compare it with the editor and click Restore this version. The Checkout simulator also runs the active script, with a checkbox under Rules to run to compare results with and without it.
If the script fails
An error, going over 5 ms of CPU, or returning something that isn't a valid list of rates counts as a failure. Pick what that checkout gets in If the script fails: Use zone rates (the default: the rates your zones and combined rates made) or Show a backup rate with a name and price you set. Only the failing checkout uses the fallback. The script is never switched off automatically, so a shopper's unusual cart can't turn it off for everyone.
Limits
| Limit | Value |
|---|---|
| Scripts per store | 10, one active |
| Code size | 100 KB |
| CPU time per run | 5 ms (2 seconds of wall time at most) |
| Network access | None: no fetch, no external APIs |
| Rates returned | 20 |
| Script answer | 128 KB |
| console.log output kept | 20 lines of 200 characters |
| Versions kept | Last 50 per script |
| Metafields | 10 product and 10 variant |
| Test runs and save checks | 10 a minute per store |
Plan and requirements
- Scripts are part of the Pro plan ($19.99/month or $191.88/year, 7-day free trial). Lite stores see the editor with a Pro banner and can't save.
- Switching from Pro to Lite is blocked while a script is active. Deactivate it first, along with deleting zones and combined rates.
- Saving a store's first script connects the app's rates to checkout, the same as saving a first zone. Carrier Calculated Shipping must be available on the store; see Carrier Calculated Shipping.
- Uninstalling the app deletes your scripts and their versions. Copy the code somewhere first if you may reinstall.
Write your own shipping rate logic
Shipping scripts are on the Pro plan. 7-day free trial. Free on development stores.
Try the app freeFrequently asked questions
›Is this the same as Shopify Scripts?
No. Shopify Scripts were Ruby, Plus-only, and stopped running on June 30, 2026. SMART shipping scripts are JavaScript that runs on the app's own rates, and they can raise or lower prices and add rates. Shopify's shipping scripts could only hide, rename, reorder, and discount existing rates.
›Can a script change Shopify's own rates or another app's rates?
No. A script works on the rates this app sends to checkout. Use Delivery customizations to hide, rename, or reorder rates from Shopify or other apps.
›Can a script call an API or read my inventory?
No. Scripts have no internet access. They can read the cart, addresses, customer, totals, the zone rates, and the product data you tick in Data to load, including up to 10 product and 10 variant metafields.
›What happens if my script throws an error at checkout?
That checkout gets the fallback you picked in If the script fails: your zone rates or one backup rate. The next checkout runs the script again.
›Which plan do I need?
Pro, with Carrier Calculated Shipping available on the store. Delivery customizations alone work on Lite.