The API manages three things. They’re closely related, and knowing which one you want is most of the battle.

Marketplace listing

A live offer to sell a bottle. Visible and purchasable.

Draft listing

A staged listing, visible only to the selling pharmacy. Not purchasable.

Cash offer

An offer to buy a medication at a stated price.

Marketplace listings

An offer to sell a particular bottle of medication. POST /api/v1/marketplace-listings creates one; sellingPharmacyNpi determines which pharmacy is selling. A listing can be updated with PUT or cancelled with DELETE only while it is Pending — that is, unsold. Once purchased, both return 409 Conflict. Two fields deserve attention beyond their validation rules.

DSCSA certification

dscsaCertified must be true. It is not a formality — it records the selling pharmacy’s attestation that:
The item has been stored and handled under the manufacturer’s temperature and storage requirements, was not purchased using a government discount program (i.e. 340b) or preferred pricing, is not restricted to a limited distribution network, and was acquired from a manufacturer or wholesaler in compliance with the Drug Supply Chain Security Act (DSCSA). Seller further must agree that if asked to provide DSCSA T3 data in support of these facts, such data will be provided. Seller further must agree that InStockRx will deduct transaction fees of 10% of the purchase price, minus any credits, as applicable, if this medication is sold to a buyer. Seller must agree that you are authorized to sell this medication under the DSCSA and affirm that all of the required representations are true.
You are sending this attestation on behalf of the pharmacy. Make sure your integration gives them a way to make it knowingly, rather than defaulting it to true in code.

State restriction

showOnlyInState, when true, limits buyers to pharmacies in the same state as the seller. The created listing returns a stateGroupId — a GUID when the restriction applies, null when it doesn’t. The request field and the response field are deliberately different.

Quantity

quantity depends on fullPackage:

The 2D scan claim

populatedByTwoDScan records that the listing’s pedigree — gtin, serialNumber, lotNumber and expirationDate — was captured by scanning the package’s 2D barcode rather than typed in by hand. It is not a cosmetic flag. Elsewhere on the platform a buyer can post a cash offer that only a scanned listing may fill, so the flag decides whether your listing is eligible for that kind of offer. An explicit false also costs the listing 0.1 of its rating once it reaches a terminal state, and that rating feeds the selling pharmacy’s average — so it follows the pharmacy, not just the listing. The field is writable on PUT /api/v1/marketplace-listings/{id} and readable on search results, but it is not on the create body. A listing created through v1 therefore starts with no claim either way; set it with an update if a scan populated the pedigree.
Some medications can no longer be listed through POST at all. When the drug package requires a scan, or the selling pharmacy is configured to require one, creation demands populatedByTwoDScan: true — and the v1 create body has no such field, so the request is rejected with 400 and the message “For medication with NDC: a 2D barcode scan is required”.This is new: the requirement used to be enforced only in the web app, and became a server-side rule in the 1 September release. Nothing about your request changed — the same POST that worked in August now returns 400 for those NDCs and pharmacies. There is no v1 workaround; the listing has to be created in the portal.
On update, v1 stores the claim without checking it. The app-side flow rejects populatedByTwoDScan: true when any of the four pedigree fields is missing or the gtin is not 14 digits; the v1 endpoint does not. Sending true for a listing you typed in by hand produces a listing that claims a pedigree it does not have, and no error tells you so. Only set it when a scan really populated those fields.

Draft listings

A draft is a listing that isn’t live yet. It’s visible only to the selling pharmacy and can’t be bought, so it can be created with less information than a marketplace listing and filled in over time. The common use: the bottle is known, but the price isn’t decided. Create the draft now, set the price later, publish when ready. Pharmacy users publish drafts from the Draft Listings tab of their selling activity page, which converts the draft into a marketplace listing. Drafts are managed at /api/v1/draft-listings with the same create, update and cancel pattern.

Cash offers

An offer to buy — the mirror image of a listing. Managed at /api/v1/cash-offers. Where a listing describes a bottle you have, a cash offer describes a bottle you want, with requirements attached: requireFullPackage, requireSealedPackage, requireOriginalPackage, requireIntactPackageLabel and requireCertificationFile.
requireCertificationFile means “only fill this offer from certified listings.” When the drug package requires certification, the field is mandatory and must be true — otherwise the request is rejected.
Shipping is chosen with shippingRequestMethod. Medium requires a positive maxShippingPrice; the other supported methods require it to be null or zero, and Custom is not supported when creating a cash offer through this API. See Enumerations.
shippingRequestMethod cannot be changed on update. maxShippingPrice is validated against the offer’s existing shipping method.

Searching

All three types share a search pattern: POST to /search under the relevant path, with a searchingContext that selects the perspective. Search results are paginated with start (offset, default 0) and take (page size, default 10).

The response envelope

Every search returns the same wrapper rather than a bare array: Page by comparing start + take against recordsFiltered — data.length alone can’t tell you whether you’ve reached the end.
writePagedSearch is echoed internal bookkeeping, not a description of what v1 accepts. It carries a columnFilters array and a columns list that the v1 search bodies have no fields for and that nothing applies to a v1 search. Sending them does nothing; reading them back tells you nothing about your request. Filter with the typed fields on the search body instead.

Open returns on a listing

A marketplace listing search result carries activeReturn, which is populated only while a return is open on that listing and null otherwise:
null means “no return is open” — not “no return ever happened.” A finished return, an approved one the buyer kept, and a rejected request all read as null here. To see how a return ended, read the listing’s own status: a completed return cancels the listing with MarketplaceListingCancellationReason 5 — ReturnCompleted.