Most CCPA guides open with a revenue figure. Under $25 million, they say, and you can stop reading. Two things are wrong with that.
The figure is out of date. And revenue is only one of three tests. You need to meet just one of them.
The test that catches small teams has no revenue floor and no user count. It turns on what your site sends to other firms. For a product an AI tool scaffolded, that is a question about your script tags. Not your books.
The three tests, and the one with no floor
The CCPA uses the word "business" as a legal term. Civil Code section 1798.140(d)(1) sets it out. You are a business if you run for profit, you collect data on people in California, you do business in the state, and you meet one or more of three tests.
| Test | What it says | The catch |
|---|---|---|
| A | Gross revenue above $25,000,000 last year, "as adjusted" | The real figure is $26,625,000 from 1 January 2025 |
| B | Buys, sells or shares data on 100,000 or more consumers or households a year | Households, not devices. And it is a California count |
| C | Gets 50 percent or more of yearly revenue from selling or sharing data | No revenue floor. No user count. A tiny firm can meet it |
Take test A first. The text of the law says $25,000,000. Then it adds four words that most blog posts drop: "as adjusted". The state agency raises the number for inflation every odd year. On 1 January 2025 it went to $26,625,000. If a page quotes $25 million flat, it has not been checked since 2024.
Test B was raised too. An older draft used 50,000, and counted devices. The current text reads "100,000 or more consumers or households". Devices came out. So did the word "receives". You have to buy, sell or share.
Test C is the one to read twice. It has no revenue floor. It has no user count. It asks only what share of your money comes from selling or sharing data. A firm with three staff and modest revenue can meet it. So can a free product that runs on ad money.
The trap in plain words. Tests A and B have floors. Test C does not. So "we are too small for CCPA" is not a claim about size. It is a claim about where your revenue comes from. Those are different questions.
Why "sharing" catches you at zero dollars
Here is the word that does the damage. The CCPA defines "share" in section 1798.140(ah). It means passing a person's data to a third party for cross-context behavioral advertising. Then it adds the key phrase: "whether or not for monetary or other valuable consideration".
Read that again. No money has to change hands. The law even spells out that it covers deals "in which no money is exchanged".
Cross-context behavioral advertising has its own definition, in section 1798.140(k). It means ads aimed at someone based on what they did across other sites and products. Not the site they chose to visit. Across other ones.
That is what an ad pixel does. You drop a tag on your page. It sends a visitor ID and a page URL to an ad platform. The platform ties that to what the same person did elsewhere. You pay nothing for the tag. You have still shared.
The law names these tools by hand. Section 1798.140(aj) lists "cookies, beacons, pixel tags, mobile ad identifiers" as unique identifiers. Section 1798.140(v) puts IP addresses and browsing history in the class of personal data. None of that is a stretch or a reading. It is the text.
California's Attorney General has already run this argument and won. In August 2022 his office settled with Sephora for $1.2 million. The claim was that letting third-party firms track shoppers was a sale of their data. Sephora had not said so, and had not let people opt out. The office put it bluntly: that arrangement "constituted a sale of consumer information under the CCPA".
Sephora is a big retailer. The legal theory has nothing to do with size. It is about what the page loads.
Service provider or third party: the contract decides
Not every tag is sharing. The line falls in an odd place, and it is worth knowing.
Section 1798.140(ai) says a "third party" is anyone who is not you, not your service provider, and not your contractor. So a vendor under the right contract is not a third party. Data going to them is not a sale or a share.
What makes a vendor a service provider is the paperwork. Section 1798.140(ag) sets the terms. The contract has to stop them from selling or sharing the data. It has to stop them using it for their own ends. And it has to stop them mixing your data with data they hold on the same person from elsewhere.
That last clause is the one ad platforms fail. Mixing data across clients is the whole product. An analytics tool on a signed data agreement can sit on the right side of the line. An ad platform tag usually cannot.
So the same script tag can be fine or not fine, depending on a contract you may never have signed. If your AI tool added the tag, no one signed anything. The same gap shows up with data agreements for AI coding tools, where the vendor list grows faster than the paperwork.
This is not a theory. One of the findings against Honda in 2025 was that it had shared data with ad tech firms without producing contracts that carried the terms the law requires. The tags were there. The paperwork behind them was not.
The part that is actually code
Now the bit that no template will write for you.
If you sell or share data, you must act on an opt-out preference signal. The rule is section 7025 of the CCPA regulations. It says a business "shall process any opt-out preference signal" that meets two simple tests.
The first test is about format. The rule gives the example itself: "an HTTP header field or JavaScript object". In practice that means Global Privacy Control. A browser with GPC on sends this on every request:
Sec-GPC: 1
And it exposes this to your page script:
navigator.globalPrivacyControl === true
That is the whole signal. One header. One boolean. It arrives on the first request, before your page has drawn anything.
The second test is about the tool that sends it. It has to be clear to the user that the signal means opting out. GPC meets that. So you do not get to argue about whether it counts.
Three rules people get wrong
The link does not buy you out of it. This is the one worth pinning up. Section 7025(e) says the law does "not give the business the choice between posting the above-referenced links or honoring opt-out preference signals". Then it says it again: "Even if the business posts the above-referenced links, the business must still process opt-out preference signals." A "Do Not Sell or Share My Personal Information" link is not a substitute. It is a second duty.
It applies before you know who they are. Section 7025(c)(1) says you treat the signal as a valid opt-out for that browser or device, and for any profile tied to it, "including pseudonymous profiles". So "we cannot act on it, they are not logged in" is not an answer. The browser is the subject.
And it applies after you do know. The same rule says that if you can tell who the person is, you apply the opt-out to them as well. Not just to that one browser. So the handler has two jobs: suppress the tags now, and write the choice to the account.
What the handler looks like
The shape of the fix is small. Read the header on the server. Decide before you render whether the ad tags go in the page at all.
// server side, before render
const optedOut = req.headers['sec-gpc'] === '1';
res.locals.loadAdTags = !optedOut;
// and if the account is known
if (optedOut && req.user) {
await setOptOut(req.user.id);
}
Gating on the server matters. If you load the tag and then try to switch it off in the browser, the request has often already gone out. The safest version never puts the tag on the page.
There is a bonus. Section 7025(g) says that if you act on the signal smoothly, and you only sell or share online, you may skip the "Do Not Sell or Share" link entirely. Section 7025(f) defines smoothly: no fee, no change to how the product works, and no pop-up thrown up in reply to the signal. Do the code properly and you get fewer things to maintain, not more.
Not sure what your pages send? Our free scan reads a live URL and lists what loads. It is the same first step we take on paid work.
Read your own bundle first
You cannot answer the sharing question from memory. If a tool wrote the front end, you did not pick every tag on the page. So start by reading, not by writing.
Four steps, in order.
One. Open the network tab on a cold load. Use a fresh browser profile so no cookie is set. Load your marketing page and your product. Write down every host your page calls that is not yours. That list is your real vendor list. It is usually longer than the one in your head, and it belongs on your pre-launch checklist next to the rest of the go-live work.
Two. Grep your environment file and your front-end config. Every tracking key, ad account ID and site tag lives somewhere in the repo. Search for the vendor names you found in step one. Anything that ships to the browser is public, which is a separate problem worth reading about in API keys in AI-built front ends.
Three. For each host, answer one question: contract or no contract? If you have a signed agreement that bars them from selling, reusing and mixing your data, they are likely a service provider. If you clicked through free terms, assume third party. Write the answer down next to the host.
Four. Check whether anything reads the signal. Search your whole codebase for these two strings:
sec-gpc
globalPrivacyControl
On a product built by an AI tool, this search almost always returns nothing. Nobody asked the model for it, so the model never wrote it. That empty result is the gap, and it is the one thing on this page you can settle in ten seconds.
The same "read it, do not recall it" habit drives the record of processing activities under GDPR. Different law, same move. Your schema and your config are the source. Your memory is not.
What has actually been punished
Four public actions, and one clear pattern.
| Action | Amount | What the regulator found |
|---|---|---|
| Sephora Aug 2022, Attorney General | $1,200,000 | Did not tell people it sold their data. Did not act on opt-outs sent by a global privacy control. Did not fix it inside the 30 days then allowed |
| American Honda Motor Co. Mar 2025, privacy agency | $632,500 | Made people prove who they were, and hand over extra data, just to opt out. Privacy tool did not offer the choices evenly. Shared data with ad tech firms without contracts carrying the required terms |
| Todd Snyder, Inc. May 2025, privacy agency | $345,178 | Privacy portal was set up wrong, so opt-out requests went unprocessed for 40 days. Asked for more data than needed. Made people prove who they were before letting them opt out |
| California, Colorado and Connecticut Sept 2025, joint sweep | Ongoing | Contacting firms that do not act on GPC, and asking them to comply |
Read down the right-hand column. Not one of these is a wording problem. Every single one is a setup problem. A portal wired up wrong. A form that asked for too much. A vendor with no contract behind it.
Two of the four also turn on a rule worth saying on its own: you may not make someone prove who they are before you let them opt out. Honda and Todd Snyder were both pulled up for it.
The head of the agency's enforcement division put the wider point well when the Todd Snyder decision came out. "Using a consent management platform doesn't get you off the hook for compliance." Buying a banner tool does not move the duty off you.
And note what Honda was told about its ad tech vendors: it had shared data with them without contracts carrying the terms the law requires. That is the service provider line from earlier in this piece, tested in a real case.
All of this is checkable from outside, in a browser, in minutes. Which is why sweeps work, and why they keep happening.
Two more facts worth holding. The grace period is gone: the right to fix a breach after being caught expired on 1 January 2023. And the penalties were raised with the thresholds. They now run to $2,663 per breach, or $7,988 where it was deliberate or involved a child under 16.
There is also a private route. Section 1798.150 lets people sue over certain data breaches, for between $107 and $799 per person per incident. That is small on its own and large in a group.
Fix list
- Work out which of the three tests you meet. Check test C last and hardest.
- Use $26,625,000, not $25 million.
- List every non-you host your pages call on a cold load.
- Sort each one: service provider under contract, or third party.
- Read
Sec-GPCon the server. Decide there whether ad tags render. - Apply the opt-out to the browser, and to the account when you know it.
- Never ask for ID to process an opt-out.
- If you keep a "Do Not Sell or Share" link, keep the header handler too. The link alone is not enough.
- Wait at least 12 months before asking an opted-out user to opt back in.
What a scan can and cannot tell you
We should be straight about the limits here.
An outside scan reads what your pages send. It can list the third-party hosts. It can set the GPC header and watch whether the tags still fire. Our Compliance Score has a check for exactly that, named CCPA opt-out signal detection, alongside checks for tracking pixels without consent and for cookie consent loading before tracking scripts.
What no scan can see is your paperwork. Whether a given vendor is a service provider turns on a contract sitting in someone's inbox. Nor can a scan read your books, so it cannot tell you whether you meet test A or test C. Those are your answers to give.
And a score is not legal advice. We are engineers. We can tell you a header is ignored and a tag fires anyway. A lawyer tells you what that means for your firm. Where we can help is the habit itself, which is the point of our notes on safer AI-assisted development.
Across the 700+ AI-built apps we have audited, the average Launch Readiness Score is 44/100. Opt-out signal handling is not what drags that number down. But it comes from the same root: nobody read what the generated code actually does.
Where this sits
Two laws, two opposite defaults, and people mix them up constantly.
Under GDPR and the EU cookie rules, the default is off. You wait for a yes before non-essential tags fire. We covered that in cookie consent for AI-built apps.
Under the CCPA, the default is on. You may share until someone says stop. The duty is to hear the stop, from a link or from a header. Miss that difference and you build the wrong thing twice.
The rest of the ground is close by. Deletion duties sit in the right to erasure, access requests in the DSAR checklist, and the wider European picture in our GDPR guide for AI-built products. The data layer these rules land on is often a Supabase project with weak row-level security, which is worth fixing first.
If you want the paperwork built rather than explained, that is the Compliance Wing. If you want the code side read by an engineer, that is the Launch Readiness Audit. Prices and scope are on pricing, and the full set of guides is in the blog index.
FAQ
Does the CCPA apply to my SaaS if I earn under $25 million?
Maybe. Revenue is only one of three tests, and you need to meet just one. First, the figure is no longer $25 million. It rose to $26,625,000 on 1 January 2025, because the law adjusts it for inflation. Second, a separate test asks whether you get half or more of your revenue from selling or sharing data. That test has no revenue floor and no user count. A very small firm can meet it. A third test covers buying, selling or sharing data on 100,000 or more California consumers or households in a year. Check all three, not just the money one.
Is a free ad pixel a "sale" of personal information?
It may well be a "share", which carries the same opt-out duty. The CCPA defines sharing as passing data to a third party for advertising that tracks people across other sites. The law adds that this counts "whether or not for monetary or other valuable consideration", and covers deals where no money is exchanged. So paying nothing for the tag does not help. California's Attorney General settled with Sephora for $1.2 million in 2022 on the view that letting third-party firms track shoppers was a sale of their data.
Do I have to honor the Global Privacy Control signal?
Yes, if you sell or share personal data. Section 7025 of the CCPA regulations says a business shall process any opt-out preference signal that meets its rules. GPC meets them. It arrives as an HTTP header, Sec-GPC: 1, and as a browser property your scripts can read. You must treat it as a valid opt-out for that browser or device, including for profiles that are not tied to a name. If you can tell who the person is, you must apply it to their account too.
Is a "Do Not Sell or Share My Personal Information" link enough on its own?
No, and the rules say so directly. Section 7025(e) states that the law does not give a business a choice between posting the link and honoring opt-out signals. It adds that even where the business posts the link, it must still process opt-out preference signals. The link and the signal handler are two separate duties. There is one exception that runs the other way: if you process signals smoothly, and you only sell or share online, you may be able to drop the link.
What is the difference between CCPA opt-out and GDPR cookie consent?
The default. Under GDPR and the EU cookie rules, non-essential tracking waits for a yes. Nothing should fire until the visitor agrees. Under the CCPA, sharing is allowed until someone opts out, and your duty is to hear that opt-out when it comes. So one law asks you to block by default, and the other asks you to listen by default. A product serving both audiences needs both behaviours, not one setting that tries to cover them.
How would anyone find out that my site ignores the signal?
By loading your site with the signal turned on and watching what happens. That is all it takes, and regulators are doing it. In September 2025 the California privacy agency, with the Attorneys General of California, Colorado and Connecticut, announced a joint sweep of firms that do not act on GPC. Earlier decisions came from the same kind of outside check. Todd Snyder was fined $345,178 after a misconfigured privacy portal left opt-out requests unprocessed for 40 days. Honda was fined $632,500 over a set of failures that included making people verify themselves before they could opt out. These are findings about how a site behaves, not about how a policy reads.
Research sources
- California Privacy Protection Agency: Updated Monetary Thresholds in CCPA, reference for the inflation-adjusted figures: the annual gross revenue threshold in the definition of business rising to $26,625,000 effective 1 January 2025, the administrative fine amounts of $2,663 and $7,988, and the statutory damages range of $107 to $799.
- California Civil Code section 1798.140 - Definitions, reference for the three thresholds in section 1798.140(d)(1), the definition of share in 1798.140(ah) including the phrase whether or not for monetary or other valuable consideration, cross-context behavioral advertising in 1798.140(k), third party in 1798.140(ai), service provider contract terms in 1798.140(ag), and unique identifiers including cookies, beacons and pixel tags in 1798.140(aj).
- Cal. Code Regs. Tit. 11, section 7025 - Opt-Out Preference Signals, reference for the duty to process any opt-out preference signal, the HTTP header field example in 7025(b)(1), the pseudonymous profile rule in 7025(c)(1), the statement in 7025(e) that posting the link does not replace honoring the signal, and the frictionless conditions in 7025(f) and (g).
- California Attorney General: Settlement with Sephora as Part of Ongoing Enforcement of the California Consumer Privacy Act, reference for the $1.2 million settlement of August 2022, the finding that allowing third-party tracking constituted a sale of consumer information, and the expiry of the 30-day right to cure on 1 January 2023.
- California Privacy Protection Agency: Joint Investigative Privacy Sweep - CA, CO and CT Investigate Businesses Refusing to Honor Opt-Out Requests, reference for the September 2025 tri-state sweep by the Agency and the Attorneys General of California, Colorado and Connecticut of businesses not acting on the Global Privacy Control.
- California Privacy Protection Agency: Honda Settles With CPPA Over Privacy Violations, reference for the $632,500 fine of March 2025 and for the Enforcement Division allegations, including requiring consumers to verify themselves and provide excessive personal information in order to opt out, a privacy management tool that did not offer choices symmetrically, and sharing personal information with ad tech companies without producing contracts containing the necessary terms.
- California Privacy Protection Agency: CPPA Orders Clothing Retailer Todd Snyder to Pay Six-Figure Fine, Overhaul Privacy Practices, reference for the $345,178 fine of May 2025, for the finding that a misconfigured privacy portal left opt-out requests unprocessed for 40 days, for the findings on excessive information and identity verification before opt-out, and for the Enforcement Division statement that using a consent management platform does not remove the business's own compliance duty.
- Jai Mittal, Founder & CTO, Launch Ready Code — Proprietary data from 700+ AI-built app security audits, 2025–2026. Average Launch Readiness Score: 44/100.