Tariff Math for Two-Layer Prototype PCBs: OshPark vs Imported Fabs

A comparative analysis of total pricing for small-run PCBs from OshPark, PCBWay, and JLCPCB, with a quick overview of U.S. electronics tariffs.

Introduction

If you’re a US-based hobbyist, consultant, or small-batch hardware developer, PCB manufacturing has just changed a bit. Back in 2018 when the first batch of Chinese tariffs were introduced we got hit with a surprise bill and an import nightmare (well minor inconvenience but it felt dramatic at the time). Those tariffs landed during a government shutdown and a CBP computer glitch so our boards ended up doing two laps of the globe before we got them. I use a lot of 2 layer boards as I prefer them to just about any amount of handwiring. With the various price cliffs that have been introduced I was interested in what the most cost effective way to get boards are and maybe to justify an SV2 Pro. That’s a joke but The ION Prototyping lab does have a V-One, maybe it’s time to learn how to use it!

This post visualizes the total cost difference between OshPark (domestic) and two popular China-based fabs — PCBWay and JLCPCB — for small 2-layer boards, and gives a basic overview of the various pricing cliffs.

What’s Changed?

If you’ve been ordering batches of boards and assemblies under $800 then you have had a cheap and easy life thanks to the (now dead) de minimus exemption A.K.A Section 321. Your package would pass through customs quickly and easily, with no tariff due. Once you exceeded the $800 limit then a 25% tariff became due and you’d need to write DHL a check and you need to actually deal with customs. DHL does a great job as a broker but they’ll only front so much of the tariff. Once the order value exceeds DHL’s simplified processing limits, you should probably be talking to a customs broker. For orders under $2500 there is a simplified process with a full customs process above that.

Postal shipments have a minimum tariff to simplify processing packages but the commercial carriers just calculate the tariff.

DHL Estimates

For JLCPCB and PCBWay I assume DHL shipping and the 55% tariff in place as of 2025-05-15. I am getting the DHL processing fees from their online calculator.

Keep in mind: if duties aren’t paid promptly, additional fees will be added to the bill.

Declared ValueDutiesFeesDHL FeeTotal Landed Cost
$100$55$2.62$17$174.62
$1,000$550$2.62$17$1,569.62
$10,000$5,500$34.64$110.69$15,645.33
  • Tariff burden scales linearly, and is proportional to the invoice value (not just material cost).
  • DHL fees also scale, but remain a small fraction except for very cheap deliveries.

The exact fee breakdown is included at the end of this post.

Price Comparison: OshPark vs Imported Fabs

We analyzed board pricing across three suppliers for two configurations using the extimated DHL costs.

  • OshPark – US-based aggregator, known for ENIG finish and high-quality standards. Pricing assumes $5 for 3 square inches for boards ≤100 in², then $1/in² above that. They have free shipping and 100% made in the US.
  • PCBWay – Chinese manufacturer. They do everything.
  • JLCPCB – Another Chinese fab with ultra-low pricing. They do also do everything but they do it cheaper and faster than anyone.

I’ve gotten dozens of boards from each of these suppliers with no serious complaints.

Cheapest Possible 2-Layer Board

This plot looks at the cheapest 2-layer board offered. If you need a wiring board this is reasonable but puts OshPark at a disadvantage in cost as they are using more expensive plating and board materials.

SupplierFinishFR4 TGMin Drill SizeTrace/SpaceVias
OshParkENIGTG1750.254 mm6/6 milTented
PCBWayHASLTG1500.30 mm6/6 milTented
JLCPCBHASLTG1400.30 mm6/6 milTented

PCB Cost Comparison Plot

OshPark Comparable 2-Layer Boards

This plot looks at the the Chinese fabs 2-layer boards that have comparable specs to OshPark. The ENIG is great for asthetics but these are 2-Layer boards, it’s unlikely you’re placing fine pitch parts on them. OshPark has 3-5um gold plating which can’t be changed whereas PCBWay offers 1, 2, or 3um options and JLCPCB offers 1 or 2 um. The different thicknesses can be visually distinguished.

While the previous plot compared the cheapest 2-layer options, it’s a bit unfair to OshPark. Their default spec includes ENIG finish and TG175 material — things you’d need to pay extra for at JLCPCB or PCBWay.

This second comparison levels the playing field.

SupplierFinishGold ThicknessFR4 TGMin Drill SizeTrace/SpaceVia Treatment
OshParkENIG3–5 µmTG1750.254 mm6/6 milTented
PCBWayENIG3 µmTG1750.25 mm6/6 milTented
JLCPCBENIG2 µmTG1400.25 mm6/6 milPlugged

PCB Cost Comparison Plot

Summary

If you’re ordering low total board area and don’t need custom solder mask colors, OshPark avoids the tariff headache and is often cheaper. For projects with larger total board areas, imported fabs like JLCPCB and PCBWay win on price — even with the increased tariff.

If you want ENIG and a high TG FR4 then OshPark is competitive up to significant volumes. Note that OshPark only offers 2x solder mask varieties where as the others offer several colors for a fairly nominal fee.

References

Have thoughts, tricks for minimizing tariffs, or manufacturing insights? Contact me!

Appendix

DHL Import Cost Calculator

Options

EXPORT TARIFF CODE8534.00.9000
IMPORT TARIFF CODE8534.00.0040
Duties Paid ByReceiver
Shipper is aBusiness
Receiver is aBusiness

$0 - $800 Value ($100)

  • Condition: L4 - Low Value Shipment of value up to USD 2500
  • Exempted: Harbour Maintenance Fees
Duty DescriptionRateAmount (USD)
Additional Duty (Section 301)25% Total Value of Product$25.00
Custom Duty0% Total Value of Product$0.00
Additional Duty (Executive Order/IEEPA - Reciprocal Tariff)10% Total Value of Product$10.00
Additional Duty (Executive Order/IEEPA)20% Total Value of Product$20.00
Fee DescriptionRateAmount (USD)
Harbour Maintenance Fees0$0.00
Merchandise Processing FeesUSD 2.62$2.62
DHL Fee DescriptionRateAmount (USD)
Duty Tax ProcessingDHL Express Rate$17.00

Landed Cost for $100: $174.62

$800 - $2500 Value ($1000)

  • Condition: L4 - Low Value Shipment of value up to USD 2500
  • Exempted: Harbour Maintenance Fees
Duty DescriptionRateAmount (USD)
Additional Duty (Section 301)25% Total Value of Product$250.00
Custom Duty0% Total Value of Product$0.00
Additional Duty (Executive Order/IEEPA - Reciprocal Tariff)10% Total Value of Product$100.00
Additional Duty (Executive Order/IEEPA)20% Total Value of Product$200.00
Fee DescriptionRateAmount (USD)
Harbour Maintenance Fees0$0.00
Merchandise Processing FeesUSD 2.62$2.62
DHL Fee DescriptionRateAmount (USD)
Duty Tax ProcessingDHL Express Rate$17.00

Landed Cost for $1000: $1,569.62

$2500+ Value ($10000)

Duty DescriptionRateAmount (USD)
Additional Duty (Section 301)25% Total Value of Product$2500.00
Custom Duty0% Total Value of Product$0.00
Additional Duty (Executive Order/IEEPA - Reciprocal Tariff)10% Total Value of Product$1000.00
Additional Duty (Executive Order/IEEPA)20% Total Value of Product$2000.00
Fee DescriptionRateAmount (USD)
Harbour Maintenance Fees0$0.00
Merchandise Processing Fees(0.3464% Total Value of Product) — Minimum $32.71, Maximum $634.62$34.64
DHL Fee DescriptionRateAmount (USD)
Duty Tax ProcessingDHL Express Rate$110.69

Landed Cost for $10000: $15,645.33

Source Code

from matplotlib import pyplot as plt
import numpy as np
oshpark_price_lv = 5/3  #  $5/sqin for 3x boards
oshpark_price_hv = 1  #  $1/sqin


# 0.25mm Hole, all ENIG, 6/6 mil traces

# TG175, ENIG 3um
pcbway_prices_tg150_hasl = (
    (5, 10, 21),
    (15, 14.8, 21),
    (100, 42, 21),
    (200, 52, 25),
    (300, 59, 25),
    (500, 86, 27),
    (1000, 180, 41),
)

# TG175, ENIG 3um
pcbway_prices_tg175_enig = (
    (5, 116.71, 21),
    (15, 117.21, 21),
    (100, 141.19, 21),
    (200, 154.34, 25),
    (300, 162.61, 25),
    (500, 194.23, 27),
    (1000, 292.3, 41),
)

jlcpcb_prices_tg140_hasl = (
    (5, 2.1, 23),
    (20, 5.4, 23),
    (50, 10.7, 23),
    (100, 13.1, 23),
    (200, 12.1, 32),
    (300, 23, 32),
    (500, 32.8, 40),
    (1000, 57.3, 56),
)

# TG140, ENIG 2um, plugged vias
jlcpcb_prices_tg140_enig = (
    (5, 80.92, 23),
    (20, 84.95, 23),
    (50, 90.35, 23),
    (100, 95.07, 23),
    (200, 104.81, 32),
    (300, 114.35, 32),
    (500, 133.62, 40),
    (1000, 181.72, 56),
)


oshpark_shipping = 0
tarriff_fraction = 0.55
fees = 11.78  #  Maximum Customs surcharge under $2500

def calculate_dhl_fees(duty):
    return 1.31 + max([17, 2/100*duty])

def get_osh_prices(area):
    unit_price = oshpark_price_lv
    if area > 100:
        unit_price = oshpark_price_hv
    return area*unit_price + oshpark_shipping

def get_imported_prices(price_matrix):
    areas = [x for x, *_ in price_matrix]
    prices = [price*(1+tarriff_fraction) +
             fees + shipping + calculate_dhl_fees(price*tarriff_fraction)
             for x, price, shipping in price_matrix]
    return areas, prices

xlims = [0, 350]
ylims = [0, 350]

x = np.linspace(3, 1000, 50)
fig, ax = plt.subplots()
ax.plot(x, [get_osh_prices(pt) for pt in x], color="purple", label="Oshpark (ENIG, TG175)")
ax.plot(*get_imported_prices(pcbway_prices_tg150_hasl), 'x--', color="green", label="PCBWay (HASL, TG150)")
ax.plot(*get_imported_prices(jlcpcb_prices_tg140_hasl), 'x--', color="blue", label="JLCPCB (HASL, TG140)")
ax.set_title("OshPark vs. Imported Prices for Cheapest 2-Layer\n1x1 inch PCB Offering (May 15, 2025)")
ax.set_xlabel("Total Board Area (in$^2$)")
ax.set_ylabel("Total Price w/ Tariff (USD)")
ax.set_xlim(xlims)
ax.set_ylim(ylims)
fig.legend(loc=(0.5, 0.15))

x = np.linspace(3, 1000, 50)
fig, ax = plt.subplots()
ax.plot(x, [get_osh_prices(pt) for pt in x], color="purple", label="Oshpark (ENIG 3-5um, TG175)")
ax.plot(*get_imported_prices(pcbway_prices_tg175_enig), 'x--', color="green", label="PCBWay (ENIG 3um, TG175)")
ax.plot(*get_imported_prices(jlcpcb_prices_tg140_enig), 'x--', color="blue", label="JLCPCB (ENIG 2um, TG140)")
ax.set_title("OshPark vs. Imported Prices for Comparable 2-Layer\n1x1 inch PCB Offering (May 15, 2025)")
ax.set_xlabel("Total Board Area (in$^2$)")
ax.set_ylabel("Total Price w/ Tariff (USD)")
ax.set_xlim(xlims)
ax.set_ylim(ylims)
fig.legend(loc=(0.5, 0.15))