๐ŸŽ‰ Premium Proxies ยท 3-Day Free TrialClaim Now โ†’
Use Cases

Proxies for Freight and Logistics Rate Monitoring

Freight rate monitoring fails when you treat a quote like a price. Model expiry, split the surcharge stack, and know when to buy the data instead.

S SparkProxy 1 24 min read
Share
Proxies for Freight and Logistics Rate Monitoring

Freight rate monitoring works only if you model every rate as a quote with an expiry and an itemised surcharge stack, collect the genuinely public layer (indices, tariff notices, public rate calculators) through geo-targeted proxies, and buy contract-rate data instead of trying to scrape the portals it lives behind.

Retail price monitoring is an easier problem than most logistics teams expect it to be. A product page shows one number, that number is true until the seller changes it, and two observations of the same SKU are comparable by definition. A freight rate is none of those things. It gets produced on demand from an origin and destination pair, an equipment type, a weight and a dimensional weight, a service level, a validity window, and a stack of surcharges that each move on their own schedule. Compare two of those numbers without matching every one of those attributes and you have measured nothing.

Why a freight rate is not a price

The distinction sounds pedantic until it corrupts a dataset. Here is what actually differs:

PropertyRetail priceFreight rate
How it existsPublished on a pageGenerated on request from shipment inputs
IdentityOne SKUOrigin, destination, equipment, service, weight and dimensions, commodity
Truth windowUntil the seller changes itA stated validity period, commonly 7 to 30 days
CompositionOne number, tax sometimes separateA base rate plus anywhere from 3 to 15 named surcharges
Who sees itEveryoneDepends on account, contract tier, volume commitment
Where it livesPublic HTMLQuote engine, customer portal, EDI or API, email, licensed index
Two observations comparable?Yes if the SKU matchesOnly if the entire quote key matches

Every freight mode expresses this differently. Full container load ocean is priced per container on a port pair. Less than container load and air are priced per chargeable unit, where air bills the greater of gross weight and volumetric weight at the IATA 1:6000 ratio, meaning one cubic metre equals 166.67 kg. Less than truckload in North America is priced by freight class and density. United States domestic parcel bills dimensional weight at a divisor of 139 cubic inches per pound. Full truckload is priced per mile with lane imbalance baked in, and rail intermodal blends the two.

The collection patterns that work for datacenter proxies for price comparison websites assume a listed price with a stable identity. Almost none of that carries over here. The closest adjacent problem on this blog is travel fare aggregation, which is also quote shaped, but a fare at least resolves to a bookable, publicly visible number. Freight quotes frequently do not.

The quote key: what must match before you compare

Treat this as a composite key. If any field is missing or different, the two rows do not belong in the same comparison. Discard the pair rather than fuzzy matching it.

FieldExample valueWhat goes wrong if it is unmatched
Origin and destinationCNSHA to NLRTM (UN/LOCODE)Port pair, not country pair. Shanghai to Rotterdam is not Ningbo to Antwerp.
BasisCY/CY, door to door, port to doorThe inland leg is often 20 to 40 percent of a door rate
Equipment or class40HC, 20GP, 40RF, NMFC class 70Reefer and dry are separate markets that move independently
Chargeable quantity1 container, 620 kg, 12 handling unitsAir and parcel bill the greater of actual and dimensional weight
Density8.2 lb per cubic footLTL classification moved to a density scale in July 2025
CommodityHS code, NMFC item numberHazmat, lithium batteries and food grade all reprice
Service levelStandard, express, guaranteed slot, priorityA premium ocean product sits on a different rate ladder
Validity window2026-08-14 to 2026-08-31A quote is not a price on the day you happen to read it
Sailing or departure weekWeek 37Two quotes read today can govern different weeks
Currency and FX timestampUSD, rate pinned dailyA converted figure is only comparable against the same pinned day
Inclusion scopeWhich surcharges sit inside the numberThe single largest source of fake variance
Account tierPublic tariff, registered, contractedPublic calculators return list rates that nobody actually pays

That density row deserves attention because it silently broke a lot of historical series. The National Motor Freight Traffic Association restructured the NMFC classification system with the first phase effective 19 July 2025, moving the majority of commodities from fixed item classes onto a density based scale. An LTL rate quoted for the same commodity in June 2025 and in August 2025 may sit in a different class entirely. Unless you stored dimensions and computed density on both sides, a year over year comparison across that date is comparing two different products.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

Modelling a rate as a quote with an expiry

A rate row needs at least three dates, and usually four. Observation time is when you collected it. Validity is the window in which the carrier will honour it. Effective date is when an announced change starts to apply, which is frequently weeks after you read the announcement.

CREATE TABLE freight_quote (
  quote_id        BIGSERIAL PRIMARY KEY,
  observed_at     TIMESTAMPTZ NOT NULL,
  source          TEXT NOT NULL,        -- index | carrier_tariff | calculator | marketplace
  lane_origin     TEXT NOT NULL,        -- UN/LOCODE
  lane_dest       TEXT NOT NULL,
  basis           TEXT NOT NULL,        -- cy_cy | door_door | port_door
  equipment       TEXT NOT NULL,        -- 40HC | 20GP | 40RF | LTL_CLASS_70 | AIR_GEN
  service_level   TEXT NOT NULL,
  chargeable_qty  NUMERIC NOT NULL,
  chargeable_unit TEXT NOT NULL,        -- container | kg | cwt | shipment
  valid_from      DATE,
  valid_until     DATE,
  validity_source TEXT NOT NULL,        -- stated | inferred
  effective_from  DATE,                 -- for announced future changes
  currency        CHAR(3) NOT NULL,
  fx_rate_date    DATE,
  base_amount     NUMERIC NOT NULL,
  all_in_amount   NUMERIC,              -- derived, never the source of truth
  account_tier    TEXT NOT NULL,        -- public_tariff | registered | contracted
  raw_ref         TEXT NOT NULL         -- pointer to the stored HTML, JSON or PDF
);

Two rules make this table trustworthy. First, all_in_amount is always derived from the base plus the surcharge rows, never written straight from a headline figure on a page, because a page's own "all in" rarely includes the same set of lines twice in a row. Second, a quote with no stated validity gets an inferred expiry per source, flagged as inferred, and it is never carried forward past that date as though it were still live.

This is ordinary bitemporal modelling, the same discipline that keeps a financial time series honest. If that pattern is unfamiliar, the reasoning in using proxies for financial data collection transfers directly: you need to answer both "what was the rate for 3 September" and "what did we believe the rate for 3 September was, as of 20 August".

The surcharge stack, and why it needs its own table

Store every line item as its own row with its own time series. Folding them into a single number destroys the only signal that matters.

Line itemCommon codeDriven byReset cadence
Base ocean freightOFR, BASLane supply and demandWeekly to monthly
Bunker or fuel adjustmentBAF, FAF, LSSFuel index, [IMO 2020](https://www.imo.org/en/MediaCentre/HotTopics/Pages/Sulphur-2020.aspx) 0.50 percent sulphur capMonthly or quarterly
EU emissions tradingETSEUA price, phase-in under [Directive (EU) 2023/959](https://eur-lex.europa.eu/eli/dir/2023/959/oj)Quarterly
FuelEU MaritimeFEUM[Regulation (EU) 2023/1805](https://eur-lex.europa.eu/eli/reg/2023/1805/oj), applies from 1 January 2025Quarterly or annual
Currency adjustmentCAFLocal currency against USDMonthly
Peak seasonPSSCapacity tightnessAd hoc, announced
General rate increaseGRICarrier pricing actionAnnounced with notice
CongestionPCS, CGSPort and terminal dwellAd hoc
Terminal handling, origin and destinationTHCTerminal tariffAnnual
Documentation and bill of ladingDOCFixedAnnual
Detention and demurrageDET, DEMFree time overrunPer event
Road and parcel fuel surchargeFSC[EIA weekly diesel price](https://www.eia.gov/petroleum/gasdiesel/)Weekly
Accessorials (liftgate, residential, inside delivery)VariousService selectedAnnual

Two things fall out of keeping these separate.

The base to surcharge shuffle

Carriers routinely hold an all-in figure roughly flat while moving money between the base rate and the surcharge lines, or announce a base reduction alongside a bunker increase that more than cancels it. A procurement team watching only the all-in sees stability. A team watching the base series sees the base falling while the recoverable surcharges climb, which is the actual negotiating position, because base rate is what you can argue about and index linked surcharges usually are not.

New line items poison year over year comparisons

The EU emissions trading extension to maritime transport began applying on 1 January 2024, with a phase-in of 40 percent of verified emissions for 2024, 70 percent for 2025 and 100 percent from 2026. FuelEU Maritime added a second new line from 1 January 2025. On any lane touching the European Economic Area, an all-in figure from 2023 and one from 2026 differ partly because two surcharge categories now exist that did not exist before. Year over year analysis on those lanes has to either exclude new codes or backfill them at zero, and say which it did.

Detention and demurrage deserve separate treatment because they are billed per event rather than per shipment, and because the United States Federal Maritime Commission's demurrage and detention billing requirements rule took effect on 28 May 2024, changing what has to appear on an invoice. Invoice-derived cost data from before and after that date is not structurally identical.

Spot rates versus contract rates

SpotContract
How it is setQuoted on request, per shipmentNegotiated, typically annual or six month
VolatilityMoves dailySteps, with surcharge pass-through in between
Public visibilityPartial, through indices and marketplacesEffectively none
ValidityDays to a few weeksA season, subject to pass-through clauses
What you can collectIndex levels, calculator quotes, marketplace quotesAlmost nothing publicly
Correct sourcePublic collection plus an index subscriptionBuy it, or read your own TMS

The mistake worth avoiding: teams build a scraping programme to answer "are we paying too much", when their own contracted rates already sit in their TMS or ERP and the market side is a licensed product. The comparison you want is internal contract against market benchmark, which needs one internal source and one purchased source. Public collection is a third leg, and its job is leading indicators rather than levels.

Which sources are actually accessible

SourceWhat it gives youAccess realityVerdict
Index landing pages ([Drewry WCI](https://www.drewry.co.uk/supply-chain-advisors/supply-chain-expertise/world-container-index-assessed-by-drewry), [Freightos FBX](https://fbx.freightos.com/), [SCFI](https://en.sse.net.cn/indices/introduction_scfinew.jsp), [Baltic Exchange](https://www.balticexchange.com/))Headline composite, sometimes a lane subsetHeadline public, full series licensedCollect the headline as a free signal, buy the series
Carrier rate announcement pages (GRI, PSS, ETS, FuelEU notices)Forward dated, itemised, authoritativeGenuinely public, no loginCollect. Best value per credit on this list.
Carrier surcharge and tariff lookup toolsPer lane surcharge valuesPublic, form drivenCollect with a scripted form interaction
Parcel and LTL public rate calculatorsList tariff rates by origin, destination, weight, dimensionsPublic, rate limited, varies by countryCollect carefully, and label it as list price
Digital forwarders and freight marketplacesIndicative spot quotesSome public widgets, most behind signupPublic widget only. Signup terms usually forbid automation.
Carrier and forwarder customer portalsYour contracted ratesAuthenticated, governed by contractDo not scrape. Ask for the API or EDI feed.
Index provider APIs (Drewry, Freightos, [Xeneta](https://www.xeneta.com/), Baltic)Clean normalised series with historyPaidBuy
Port and terminal operations pages, vessel schedulesDwell times, blank sailings, omissionsMostly publicCollect. This explains why rates moved.
Government reference series (FMC, EIA diesel, Eurostat)Fuel and trade reference dataPublic, often with an APIUse the official feed

Read that table honestly. A large share of freight spend is priced inside authenticated portals, under agreements that both restrict automated access and make the numbers commercially sensitive. Proxy infrastructure does not change that, and no amount of it produces contract rates. What collection gives you is the public tariff layer and the forward looking announcements, which is genuinely valuable and genuinely underused.

When to buy the data instead of collecting it

Buy when you need a normalised lane level series with several years of history, when the number has to survive an audit or sit in front of a carrier during a tender, when the source's terms are a contract your company signed, or when collection cost plus engineering time exceeds the subscription price. That last case is more common than the industry admits.

Collect when the data is public and forward dated, which is exactly what tariff and surcharge announcements are. Collect when no vendor covers your specific lane, mode or accessorial, which is normal for LTL, regional parcel and inland moves. Collect when you need the underlying evidence, the actual notice with its effective date, rather than a number in a spreadsheet. Collect when your required frequency is higher than the vendor's publication cadence.

Run the arithmetic before choosing. A matrix of 250 lanes across 4 equipment types is 1,000 quotes a day. Priced through a rendered, geo-targeted, form-driven quote flow at 15 credits each, that is 15,000 credits a day and roughly 450,000 a month. Priced through a batched JSON endpoint at 1 credit per batch, with escalation only when something changed, the same coverage costs a small fraction of that. If your design lands closer to the first number than the second, a subscription is probably cheaper than your pipeline. The general trade-off is covered in web scraping versus using an API, and it applies with unusual force here, because freight has mature commercial data vendors that retail pricing does not.

Geo and account tier: same lane, different quote

Carriers run country specific sites with country specific tariffs, currencies and tax display rules. The same parcel calculator queried from a United States exit and a German exit returns a different currency, different tax inclusion, and sometimes a different published tariff for an identical shipment. The exit IP is what decides which tariff you get served, ahead of any language header, so keep the two aligned. Our explainer on what geo-targeting means in proxies covers how country selection is enforced at the exit node.

Account tier is the second axis, and it is the one that misleads people. A public calculator returns the published tariff. That is a ceiling, not a market rate, and treating it as a market rate produces a series that is wrong in level while still being useful in shape.

The useful application is exposure modelling. Major parcel carriers have announced average annual general rate increases of 5.9 percent for several consecutive years, including the published 2026 rate changes. That average tells you very little about your own bill, because the increase varies widely across zones, weight bands and accessorials, and a shipper concentrated in short zones with heavy accessorial use can face something far from the headline. Price your own actual shipment profile against the published tariff before and after the change date and you get your number instead of the press release's number. That is a job public calculators plus geo-targeted collection do well.

Collecting public rate data with the SparkProxy Scraping API

All examples use the HTML API at https://scrape.sparkproxy.io/api/v1 with the key in the X-API-Key header, per the SparkProxy API documentation.

Start with the highest value target, a carrier rate announcement page. These are static, so skip rendering entirely and pay 1 credit:

curl -G "https://scrape.sparkproxy.io/api/v1" \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "url=https://carrier.sparkproxy.io/tariffs/rate-announcements" \
  --data-urlencode "render_js=false" \
  --data-urlencode "json_response=true" \
  --data-urlencode "tag=tariff/announcements/weekly"

Pull the fields out with extract_rules instead of writing a parser. The effective date is the field that matters most, and it is the one most pipelines drop:

import json
import requests

API = "https://scrape.sparkproxy.io/api/v1"
KEY = {"X-API-Key": "YOUR_API_KEY"}

rules = {
    "notice_title":   {"selector": ".notice-card h3", "type": "list"},
    "trade_lane":     {"selector": ".notice-card .lane", "type": "list"},
    "surcharge_code": {"selector": ".notice-card .code", "type": "list"},
    "amount":         {"selector": ".notice-card .amount", "type": "list"},
    "effective_from": {"selector": ".notice-card .effective-from", "type": "list"},
    "notice_pdf":     {"selector": ".notice-card a.pdf", "type": "href"},
}

r = requests.get(API, headers=KEY, params={
    "url": "https://carrier.sparkproxy.io/tariffs/rate-announcements",
    "render_js": "false",
    "extract_rules": json.dumps(rules),
    "tag": "tariff/announcements/weekly",
}, timeout=120)

print(r.json()["extracted"])

Most quote engines call an internal JSON endpoint that takes the shipment attributes as query parameters. Finding it is worth an afternoon, because it turns a 15 credit rendered form interaction into a 1 credit fetch, and it usually returns the surcharge lines already itemised instead of glued into a rendered total. The technique is covered in how to scrape hidden JSON API endpoints:

r = requests.get(API, headers=KEY, params={
    "url": ("https://carrier.sparkproxy.io/api/quote"
            "?origin=USLAX&dest=USORD&class=70&weight=1200&pallets=2"),
    "render_js": "false",
    "transparent_status_code": "true",
    "forward_headers": json.dumps({
        "Accept": "application/json",
        "Referer": "https://carrier.sparkproxy.io/quote",
    }),
    "json_response": "true",
    "tag": "ltl/USLAX-USORD/c70",
}, timeout=120)

transparent_status_code is doing real work there. It mirrors the target's own HTTP status, and it only applies with render_js=false, so a calculator throttling you with its own 429 shows up as a throttle rather than as a successful response that happens to contain no rate. Those two outcomes must never collapse into the same row.

When a quote genuinely requires a form, drive it with js_scenario. The instructions array takes one action per entry:

scenario = {"instructions": [
    {"click": "#cookie-accept"},
    {"fill": {"selector": "#origin", "value": "CNSHA"}},
    {"fill": {"selector": "#destination", "value": "NLRTM"}},
    {"fill": {"selector": "#equipment", "value": "40HC"}},
    {"fill": {"selector": "#ready-date", "value": "2026-09-08"}},
    {"click": "#get-rate"},
    {"wait_for": ".quote-result"},
    {"wait": 1500},
]}

r = requests.post(API,
    headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
    json={
        "url": "https://carrier.sparkproxy.io/quote",
        "country_code": "DE",
        "render_js": True,
        "js_scenario": scenario,
        "tag": "fcl/CNSHA-NLRTM/40HC/DE",
    }, timeout=240)

For the wide lane matrix, comma-separate the URLs. The whole batch costs 1 credit with render_js=false, and the response carries a results array with one entry per URL:

lanes = [("USLAX", "USORD"), ("USLAX", "USDFW"), ("USATL", "USMIA")]
urls = ",".join(
    "https://carrier.sparkproxy.io/api/quote?origin={}&dest={}&class=70&weight=1200".format(o, d)
    for o, d in lanes
)

r = requests.get(API, headers=KEY, params={
    "url": urls,
    "render_js": "false",
    "tag": "ltl/matrix/nightly",
}, timeout=180)

for row in r.json()["results"]:
    if row["success"]:
        store_quote(row["url"], row["body"])
    else:
        requeue(row["url"], reason=row["error"])

The country matrix, with the language header aligned to the exit country so the tariff and the copy agree:

MARKETS = {
    "US": "en-US,en;q=0.9",
    "DE": "de-DE,de;q=0.9",
    "SG": "en-SG,en;q=0.9",
    "BR": "pt-BR,pt;q=0.9",
}

def quote(url, country):
    return requests.get(API, headers=KEY, params={
        "url": url,
        "country_code": country,
        "render_js": "true",
        "forward_headers": json.dumps({"Accept-Language": MARKETS[country]}),
        "json_response": "true",
        "tag": "parcel/tariff/{}".format(country),
    }, timeout=180)

For a portfolio sweep, go asynchronous so one slow carrier site does not stall the batch. Pass a callback_url and the API returns 202 Accepted with a job_id, then POSTs the completed result to your endpoint:

requests.get(API, headers=KEY, params={
    "url": quote_url,
    "country_code": "NL",
    "render_js": "true",
    "callback_url": "https://rates.sparkproxy.io/hooks/quote-complete",
    "tag": "fcl/nightly-sweep",
})

Capture the notice itself when a surcharge changes, because carrier announcement pages get replaced rather than versioned:

pdf = requests.get(API, headers=KEY, params={
    "url": "https://carrier.sparkproxy.io/tariffs/notice/2026-09-gri-eur",
    "render_js": "true",
    "format": "pdf",
    "tag": "tariff-evidence/2026-09-gri-eur",
}, timeout=240)

with open("gri_2026_09_eur.pdf", "wb") as f:
    f.write(pdf.content)

Costs, taken from the documented credit table:

Collection tierParametersCredits
Static notice page or JSON endpoint`render_js=false`1
Batched lane matrix, one call`render_js=false`, comma-separated `url`1 total
Geo-targeted static fetch`render_js=false` plus `country_code`6
Rendered calculator page`render_js=true`5
Form-driven quote, geo-targeted`render_js=true`, `js_scenario`, `country_code`15
Hardened calculator`premium_proxy=true`, `render_js=true`, `country_code`30
Notice evidence capture`render_js=true`, `format=pdf`10

Handle failures with the same care as the data. A 530 means the scrape failed and credits are refunded, so the correct response is a requeue. A 429 returns retry_after_seconds, which you should honour rather than hammer. Never write a null rate into the quote table because a page timed out. A missing rate and an unavailable rate are different facts, and conflating them is how a system starts reporting phantom capacity shortages.

Change detection in a freight rate monitoring loop

EventDetection ruleWhat it actually means
Base rate move`base_amount` differs beyond the deadband on a matched quote keyA real market move
New surcharge codeA code appears that was not in the prior stackStructural change. Exclude from year over year until you hold 12 months.
Surcharge revisionSame code, new amount, `effective_from` in the futureSchedule it. Do not book it as today's cost.
All-in moves, base flatSurcharge sum changed, base did notThe base to surcharge shuffle
Validity window shortening`valid_until` minus `valid_from` trending downThe earliest leading indicator you can collect
Quote expired`valid_until` in the past with no replacementMark stale. Do not carry forward.
No offer returnedSource responded and returned no rateA capacity signal. This is data, not an error.
Fetch failure530, 429, timeoutRequeue. Write nothing to the rate table.

That validity row is the part most programmes miss. Before carriers raise rates on a tightening lane, they shorten how long a quote stays honourable, from 30 days to 14, then to 7, then to "on request". The amount has not moved yet, so a monitor watching only the number sees nothing at all. Duration of the validity window is cheap to capture, arrives in the same response you were already collecting, and tends to move earlier than the rate does. Track it as a first class series.

Set the deadband per mode rather than globally. Published tariffs are exact, so any difference is a real change and the deadband is zero. Negotiated and marketplace spot quotes carry rounding and FX noise, so a small percentage threshold on ocean and air prevents an alert storm. Run the comparison on effective_from where it exists rather than on observed_at, otherwise every announcement fires an alert weeks before the money actually changes.

Frequently asked questions

FAQ

Freight rate monitoring tracks the cost of moving a shipment between two points over time. It differs from retail price monitoring because a freight rate is quoted rather than published, depends on origin, destination, equipment, weight, dimensions, service level and commodity, carries an expiry date, and arrives as a base rate plus a stack of separately moving surcharges.

No, and you should not try. Contract rates sit behind authenticated customer portals under agreements that typically prohibit automated access, and they are specific to your account rather than publicly quoted. Use the carrier's API or EDI feed for your own rates, and buy a benchmark product such as a crowdsourced contract index for the market comparison.

Buy the index when you need a normalised lane level series with history that will stand up in a tender or an audit. Collect yourself when you need public forward dated tariff and surcharge announcements, coverage of lanes or modes no vendor prices, or a higher frequency than the vendor publishes. Most mature programmes do both.

Carriers operate country specific sites with local tariffs, currencies and tax display rules, and the exit IP address is normally what determines which version you are served. Geo-targeted collection from each relevant market is the only way to see the tariff a shipper in that market actually faces, which is why single-country collection understates the spread.

Store each surcharge as its own line item with its own code, amount and effective date, then derive the all-in figure rather than recording it. Carrier surcharge tracking at line-item level is what exposes a flat all-in hiding a falling base rate, and it lets you exclude newly introduced codes such as EU emissions trading from year over year comparisons.

Usually not for static tariff and announcement pages, which are plain fetches from a rotating datacenter pool. Geo-targeting matters far more than IP type here. Reserve premium residential routing for the small number of hardened rate calculators that reject datacenter ranges, since it costs 25 credits with rendering against 5 for the standard pool.

Limited-time ยท 50% off

Get 50% off your first month

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Offer ends soon โ€” claim it before it's gone

Claim Discount

About the Author

The SparkProxy Technical Team builds and operates SparkProxy's datacenter proxies, residential proxy network, and Scraping API. We work with logistics, supply chain and market intelligence teams running geo-distributed collection against carrier tariff pages, public rate calculators and index sources, and this guide reflects what survives production: quotes modelled with expiry, surcharges stored as line items, escalation only when something actually changed, and a clear line between what belongs in a scraper and what belongs in a data subscription. Full parameter documentation for every endpoint used above is at sparkproxy.io/docs/scraping-api. Questions about a specific rate collection design can go to support@sparkproxy.io.

Keep reading

Related articles

Proxies for App Store Optimization (ASO) Data

Proxies for App Store Optimization (ASO) Data

Use proxies for app store optimization to track keyword ranks, chart positions, review sentiment, and competitor releases in every country storefront.

SparkProxyยทUse Cases