Legal

Privacy Policy

Last updated: October 11, 2026
The short version

Your portfolio file — a CSV export or a PDF statement — is parsed inside your own browser, and the file itself is never uploaded: we do not receive a copy of it, and nor does anyone else. What reaches our server is taken from it. On every analysis, signed in or not, the app sends the ticker symbols it found so they can be classified, and for a fund it cannot identify at all, the description printed beside it, which we store (Section 4b). And if you are signed in, importing a portfolio saves the parsed result against your email address — the securities, how many of each, what they were worth, and the labels of the accounts they sat under — so that your next visit, and every feature that depends on what you hold, can start from where you left off instead of asking for the statement again (Section 4d).

If you are an adviser, broker, insurance agent or accountant using this tool for other people’s portfolios, Section 4e is the one to read: each client is a separate record under your email address, the name you give them is stored as you type it, your clients have no login here and cannot ask us for anything directly, and having their permission to put their finances in is your responsibility rather than something we can check.

So there is a database with portfolio data in it, and this page would rather say so than lean on a distinction you did not ask for. What is not in it: your name, your address, your date of birth, and your account numbers — we do not ask for any of them, and the account number the parser reads off your statement is dropped before anything is sent. You can delete the whole record, and Section 9 says how. Signing in needs an email address and a password, and nothing else.

1. What this tool does with your data

XRay My Portfolio is a static, client-side web application. When you upload a portfolio file — a CSV export from Fidelity or Schwab, or a PDF statement from your brokerage — it is read and parsed using JavaScript running locally in your web browser. PDF statements are read by a copy of Mozilla's pdf.js that we host ourselves, so opening one does not tell any other party that you are importing a statement. All calculations — exposure analysis, sector breakdowns, stress testing, correlation matrices, and every other feature — happen on your own device.

At no point is the portfolio file itself uploaded. There is no backend server receiving it, and no copy of the CSV or the PDF is written anywhere outside your own device. What leaves your browser is taken from it rather than being it: the ticker symbols, on every analysis; the description printed beside a fund the app cannot identify at all; and, if you are signed in, the parsed portfolio — securities, quantities, values and account labels. Section 4 sets out each case, and what is stripped out before it goes.

That is a statement about your data, not about network traffic, and the two are worth separating. The page itself does talk to our server: it counts your visit (Section 5), checks whether you are signed in, and — on every analysis — sends the ticker symbols it found so they can be classified into sectors, themes and size bands, asks our server to resolve any fund it cannot look through, sends that fund's symbol and printed description so the fund can be added to the database, and records which features you open if you are signed in (Sections 4 and 5). If you disconnected from the internet after the page loaded, the analysis would still run on your own machine, but any holding the app could not classify from its built-in database would be left unclassified, and live pricing and ratings would be unavailable.

2. Information we do not collect

We do not collect, store, transmit, or have access to:

Until October 2026 this section could say something stronger: that there was no database of portfolio data at all, and that a lawful demand for one would have found nothing to answer with. That is no longer true, and a policy that kept the sentence alive on a technicality would be worse than one that retires it. If you are signed in and you import a portfolio, we hold a list of your securities and their values under your email address, and we would have to answer a lawful order for it as any other company would. What such a record does not contain is your address or your account numbers. What it does contain is an email address and a portfolio — the securities, the quantities, the values, the cost basis, the account labels with any reference stripped out, and whatever you entered in the retirement planner. We never ask your name, but an account label can carry one, because that is how the brokerage printed it — and if the account belongs to an adviser, it also contains a client name they typed (Section 4e). That is a good deal more than a list of tickers, and it is worth saying so plainly: what it cannot do is tell anyone which institution holds it or in whose name. Signed out, there is still nothing of yours to hand over.

Separately from any of that, we accumulate reference data about securities themselves — which companies a fund holds, what sector a ticker is in. That is public information and says nothing about who holds it.

3. Account sign-up

Some features require a free account. We use this only to manage access to the tool — not to store or analyze your financial information.

When you sign up we ask one more thing: what kind of user you are — an individual, or an adviser, broker, insurance agent or accountant. It decides whether the tool shows you the client controls in Section 4e, and nothing else. Accounts created before we started asking have no answer recorded, and are treated as individuals.

When you sign up we collect your email address, used to confirm the address, to let you sign in, and occasionally for product updates you can unsubscribe from at any time. We also store your password — never in readable form. It is put through PBKDF2-SHA-256 with a random per-account salt and 100,000 iterations, and only that derived value is kept, so nobody here can read your password, and neither could anyone who obtained the stored record. We never ask for or store any brokerage, account, or portfolio information as part of sign-up.

Your email address is the key everything else sits under. It identifies you so you can sign in, and — if you import a portfolio while signed in — it is what the stored portfolio is filed against. That is not incidental to the feature, it is the feature: a portfolio not associated with your account could not be handed back to you next visit. Section 4d sets out what that record holds, and Section 9 how to delete it. Signed out, nothing from your file is stored at all, though the identifiers in Sections 4a and 4b are still sent, because classification runs either way.

You can ask us to delete your email from our records at any time, by replying to any email you have received from us — see Section 9.

4. What leaves your browser, and where it goes

The subsections below list every case in which something taken from your portfolio file leaves your browser, so there is no gap between what the marketing says and what the code does. Signed out, that is identifiers only — 4a and 4b. Signed in, it also includes the portfolio itself — 4d, and 4e if the portfolio is somebody else’s and you are their adviser. Sections 5 and 6 cover the rest of the page's network traffic — visit counting, feature tracking, signing in, and fonts.

4a. Classification — runs on every analysis

When you load a portfolio, your browser sends the ticker symbols it found to our own server (a Cloudflare Worker) which returns whatever sector, industry and market-cap/style classifications it holds for them. It sends every ticker it found, up to 200 of them, whether or not the app already recognises one — it does not check first. Holdings with no ticker at all are excluded, as the end of Section 4b explains. This happens on every analysis, whether or not you are signed in, and it is not optional. What is sent is the identifier only — "AAPL", "VOO" — never your share count, dollar value, cost basis, gain, or account name. Results are cached in your browser so the classifications survive a reload, but the list is sent again each time you load a portfolio — the cache makes the answer local, it does not stop the request.

If a fund you hold is not in the built-in holdings database, your browser may also ask our server to resolve it. Our server then fetches that fund's publicly filed holdings from the SEC's EDGAR system, may use OpenFIGI to map identifiers to tickers, and may send some of that fund's constituent ticker symbols to Finnhub to classify them by sector and market cap. Those are the fund's own published holdings, not yours — none of these services is told anything about you, your position, or even which fund you personally hold beyond the symbol being resolved. This can happen whether or not you are signed in.

Classification results are cached in your browser's local storage so repeat visits are faster. That cache holds public reference data about securities — sectors, industries, fund holdings lists — keyed by the symbols in your portfolio. It stays on your device; clearing your browser's site data removes it. See Section 4b for the one case in which part of it is sent back.

4b. Contributing a resolved fund back — automatic

When your browser successfully resolves a fund that was not in the built-in database, it sends the result back to our server so the next person holding that fund inherits it instead of resolving it again. What is sent is the fund's symbol, the list of companies it holds with their published weights, and any sector or size classifications derived along the way. This is public reference data about the fund, taken from its own regulatory filing — it contains no figure of yours and nothing that identifies you. It is sent automatically, without being asked, and the server independently re-validates everything before storing it.

Separately, when the app meets a fund it cannot identify at all, it sends that fund's ticker symbol together with the security description printed beside it in your export — for example "VANGUARD INFORMATION TECHNOLOGY ETF" — so the fund can be added to the database later. That description is text copied from the file you uploaded — a CSV export or a PDF statement. Apart from the ticker symbols in Section 4a, it is the only content from your file that is ever transmitted — and unlike those symbols, it is stored. It is not linked to you: the stored row holds the symbol, the description and a timestamp, with no email address, account or visit identifier beside it. We keep it for as long as the fund is still missing from the database, which also means a deletion request has nothing to match it against. Before it is sent we strip out a dollar, pound or euro amount, a number written with thousands separators, a number with two or more decimal places, a figure written with a percent sign, and any run of five or more digits, which is what an account or plan reference looks like — a statement sometimes prints a value or a percentage in the same place as the security name, and the example above is sent as "NH FIDELITY 500 INDEX". What that does not remove is a bare number: a share count printed as 1250, or a price printed as 44.5, would travel with the description. Security names are themselves full of numbers — "FIDELITY 500 INDEX", "2039 PORTFOLIO" — and stripping those would mangle the very names we are collecting. Holdings we had to invent an internal placeholder for, because the statement printed no ticker at all, are never sent here, and never reach the shared fund database or any third party — a placeholder is a slug of your statement's own wording, and it is not a security anybody could look up. If you are signed in it is stored with the rest of your portfolio, as Section 4d describes: under your email address, and removed when you delete your data. It is deliberately kept out of the shared register of securities we price, because that register has no owner against its rows and so cannot be deleted from.

4c. Live market data — optional, signed in

The features that fetch live market data — Performance, Income and Ratings — are optional and require being signed in (see Section 3); our server refuses the request outright without a session. When you use them, your browser sends a request to our own server (a Cloudflare Worker), which forwards it to Finnhub.io, a third-party financial data provider, using our own API key — you never need to obtain or enter one yourself. Finnhub returns public market data (stock prices, dividend yields, fundamental metrics) for the ticker symbols involved, which we relay back to your browser.

What is sent to our serverTicker symbols and the name of the data set requested, plus your session cookie to confirm you're signed in — never your share counts, dollar values, cost basis, or any other portfolio detail
What we forward to FinnhubThe same ticker symbols and the name of the data set requested, and nothing else — no share counts, dollar values, or account information ever reach Finnhub either
What we store from thisNothing about the request or response is retained beyond what's needed to serve it and enforce a shared rate limit, which expires after sixty seconds

Finnhub's use of any data sent to them is governed by their own terms of service and privacy practices, which we do not control. Finnhub is reached in two kinds of situation: when you use the Performance, Income or Ratings features, and when our server has to classify the constituents of a fund it is resolving for you (Section 4a). The first is optional and requires being signed in; the second is not optional and can happen signed out. In both cases what is involved is ticker symbols and the name of the data set requested.

4d. Saving a portfolio to your account — signed in

If you are signed in, a successful import sends the parsed portfolio to our own server, where it is stored against your email address. There is no checkbox and no save button: it happens every time, because the point of it is that your next visit does not begin by asking you for the same statement again, and that anything built on your holdings — a retirement projection, a tax estimate — is still there when you come back. The demo portfolio is never saved; it is not yours, and treating it as yours would be worse than not saving at all.

What is stored for each position is fixed by an allow-list in the page rather than by a list of exclusions, so a parser that learns a new field later cannot start sending it because nobody remembered to exclude it. The list is: the ticker symbol, the security description, the account label, the quantity, the value, the cost basis, and what kind of holding your brokerage called it — cash, a fund, a stock, an option or crypto. That last one is their classification rather than our guess, and keeping it is why a cash balance comes back as cash. Nothing else from the row travels — in particular the account number does not, even though the parser has it. The description is put through the same strip as Section 4b before it is stored, because a statement's description column sometimes runs into the one beside it: currency amounts, percentages and any run of five or more digits, which is the shape of an account or plan reference, are removed first. The account label is then stripped of anything shaped like a reference before it is written, so an account your statement calls “Traditional IRA …4321” is stored as “Traditional IRA”; a label that is nothing but digits becomes “Account”.

Stored beside the positions are the date printed on your statement, the date you imported it, and the portfolio total. Imports are filed by statement date: importing the same statement twice replaces the earlier copy, and importing a statement with a different date keeps both. So if you import every month, what accumulates is a record of what you held at each of those dates. Deleting (Section 9) removes all of them at once.

Whatever you enter in the retirement planner is stored the same way and under the same email address — the figures and the choices you typed, so the plan is still there next time. It is kept as you typed it.

None of this is sold, shared, or sent to any third party. A stored portfolio is never sent to Finnhub or to anyone else; the market-data requests in 4c are driven by ticker symbols, which are sent whether or not anything is stored. It exists so the tool can show your portfolio back to you, and for nothing else.

4e. Using the tool for other people’s portfolios — advisers and other professionals

If you told us at sign-up that you are an adviser, broker, insurance agent or accountant, the tool lets you keep a separate portfolio for each of your clients and switch between them. Everything in Section 4d applies to each of those portfolios unchanged — the same allow-list, the same stripping of account numbers and references, the same storage, the same deletion. This section is about the part that is different.

The record is yours, not theirs. Every client you create is stored under your email address. There is no account for your client, they cannot sign in, and we have no way to recognise them: the only things they ever see are a PDF you produce and the screen you show them. That is a deliberate limit rather than a feature we have not built yet, and it has a consequence worth stating plainly: if your client wants to know what is held about them, or wants it deleted, they have to ask you. We cannot identify their data as theirs, because to us it is yours, and we will not act on a request from somebody we have no relationship with.

The name is yours too. You name each client, and what you type is stored as you typed it. It appears in the client list, in the header while you are working, and on the cover page of any PDF you produce for them. We do not ask for it, we do not check it, and we do not need it to be a real name — a reference or an initial works as well for the tool as a full name does. If you type a full legal name, then contrary to the rest of Section 2, there is a name in our database, and you put it there. If that matters to you, use a reference.

Permission is yours to have. Putting another person’s holdings, retirement plan, business valuation or property into an online tool is something you need their agreement for. We have no relationship with them, no way to ask them, and no way to verify what you have been given permission to do. The professional obligations you are under — your regulator’s, your firm’s, your engagement letter’s — are yours, and nothing on this page alters them.

What is stored for each client. The parsed portfolio exactly as Section 4d describes it; whatever you entered in the retirement planner for them; and any assets outside the portfolio you recorded — a business, a property, a debt — with the values and assumptions you gave. Each is filed against the client, not against you in general, so switching clients shows only that client’s figures. The same separation applies on your own device: the browser keeps a separate copy per client, and signing out removes every client’s copy, not just the one on screen.

Removing a client. Archive takes a client out of your list and deletes nothing — it is for a relationship that has ended where you still have records to keep. Delete erases that client’s stored statements, their plan and their outside assets, from our server and from your browser, immediately and with no way back; it asks you to type their name first, because it sits next to Rename. Delete my data (Section 9) removes every client at once, along with your own record.

None of this changes what we do with the data: a stored portfolio exists so the tool can show it back to whoever is signed in, and for nothing else. It is not sold, not shared, and not used to profile anybody — you or them.

5. Usage analytics

We measure two things: how many people visit, and which features get used. Visit counting carries no name, no email address and no cookie — only a random identifier your browser invented, which the fourth paragraph below explains. Feature usage is not, for signed-in users — if you are signed in, opening a feature records that feature and the time against your account. This page used to add that neither could be linked to the contents of your portfolio, because those contents never reached us. Now that a stored portfolio sits under the same email address as the feature log, that is a claim about our conduct rather than an impossibility, and it is made as such in the commitments at the end of this section.

We count visits ourselves, on our own server. When you open the site, your browser generates a random identifier — a short string with no meaning anywhere outside this site — keeps it in your browser's local storage, and sends it to us together with which page you opened. That is the whole contents of that request. The same identifier is also sent, at most once per browsing session for each, when you reach one or two milestones — such as loading a portfolio for the first time, or loading the demo. Those pings record that the milestone happened, never anything about the portfolio itself. We do not store your IP address alongside that identifier, we do not derive a device fingerprint, and nothing about your portfolio is involved — these pings carry a milestone and nothing else. Section 4 lists every case in which something from your portfolio leaves your browser; none of it is involved in these pings. Your IP address is used for one thing only: a short-lived counter, kept for at most an hour and then discarded, that stops a single network flooding these counts or our lookup endpoints. It is never joined to the visit record, to your account, or to anything else.

Feature usage is recorded differently, and in one of two ways depending on whether you are signed in. If you are signed in, opening a tab or feature sends its name to our server, where it is stored against your email address with a timestamp. If you are not signed in — including when you are looking at the sample portfolio — the feature's name is sent against the same random identifier the visit counter uses, and no email address is involved, because there is none to involve. The two are counted and reported separately, so the same person is never counted in both. This is how we see which parts of the tool are actually used and which are not worth maintaining. In both cases what is recorded is the feature's name and when, never anything it displayed.

If you reached this site from a link in an email we sent, that link may carry a short campaign tag — something like oct-intro — identifying the send, never you. It is recorded once, against the same random identifier, so we can tell which message brought people here. The tag is restricted to a short plain-text label by design: a per-recipient code would make that identifier traceable back to your email address, which is precisely what the rest of this section promises it is not.

The visit counter exists to answer one question: how many people try the tool without ever creating an account. If you do later sign up, that identifier is recorded alongside your email address so we can tell "looked and left" apart from "looked and signed up". That association stays internal to us and is never sold or shared. Clearing your browser's site data deletes the identifier; your next visit simply counts as a new one.

We may add or change analytics tools over time — for example, to better understand which specific buttons or screens people use, or how visitors move from browsing for free to signing up — as the product grows. Regardless of which specific tool is in use at any given time, we commit to the following, and we will not change these commitments without updating this page and, for account holders, notifying you directly by email (Section 8):

Your browser's local storage holds fifteen things. Six of them are about you or your account: the visit identifier described above; a flag recording that this browser has signed in before, so the page knows whether to wait for a stored portfolio before drawing; a flag recording that your account has a stored portfolio, which is what the home page reads to decide where a returning visit lands; a flag recording that we have already told you a portfolio was saved; a short one-way digest of the email address this browser last signed in with, which is how signing in as somebody else clears the previous person's answers from the device instead of showing them to you — it is a digest rather than the address itself, it is never sent to us, and it is removed when you sign out; and, if you reached us from one of our emails, the campaign tag described above. Four are things you typed: any account-grouping rules you create; everything you have entered in the retirement planner — ages, incomes, pensions, spending plans; any assets you recorded outside the portfolio, such as a business or a property; and which sections you last chose to include in a PDF. All four are kept here so the page still has them after a reload. A fifth, the fund holdings you enter by hand when the app could not resolve a fund, is listed with the working data below instead: it describes a FUND rather than you, and it is the one thing in this list that is not cleared when you sign out. The last five are working data: the cached classification described in Section 4a, a note of which classifications this browser has already contributed back, the fund holdings you entered by hand, a timestamp for when the dividend-yield database was refreshed, and a flag recording whether this browser has already filed its older entries under a client.

If you hold portfolios for clients (Section 4e), those four are kept once per client rather than once per browser, so switching clients does not show you the previous one’s answers. Signing out removes every client’s copy of all four. The hand-entered fund holdings are deliberately not among them: what a fund holds is the same fact whoever is looking at it.

Of all of that, only the visit identifier and the campaign tag are sent to us as a matter of course; the cached classification only in the narrow case in Section 4b; and the planner’s answers — together with any assets you recorded outside the portfolio — only if you are signed in, under Section 4d. Clearing your browser's site data removes every one of them, and removing the planner's answers from this browser does not remove the copy saved to your account — Section 9 is how you do that.

6. Hosting infrastructure

This website is hosted on Cloudflare Pages, a static-site hosting service. Cloudflare may log standard, anonymized web server metadata (such as IP address, browser type, and timestamp) for security and performance purposes, consistent with their role as our hosting provider. Cloudflare also provides the server that receives a saved portfolio and the database it is stored in. In that role Cloudflare is our infrastructure provider rather than a separate recipient of your data: it is not given the data for its own purposes and does not use it. See Cloudflare's privacy policy for details on their hosting-level practices.

Every page on this site, including this one, loads its typefaces from Google Fonts. Requesting those files tells Google your IP address and browser user-agent, as any request to any server would. We send Google nothing else, and nothing about your portfolio is involved. If you would rather not make that request, a content blocker set to block fonts.googleapis.com will stop it; the site remains fully usable in a fallback typeface.

Email we send you — the confirmation link when you create an account, and any password-reset link — is delivered through Resend, a third-party email provider. Resend receives your email address and the contents of that message so that it can deliver it, and nothing else; it is never given anything from your portfolio. Its handling of that address is governed by its own privacy practices, which we do not control.

7. Children's privacy

This tool is intended for use by adults — managing their own investment portfolios, or, for the professionals in Section 4e, their clients’. It is not directed at children under 13, and we do not knowingly collect information from children. If we learn an account was created by a child under 13, we will delete the associated email address.

8. Changes to this policy

If this policy changes, we will update the "Last updated" date at the top of this page. If you have an account with us, we will also notify you by email of any material change — in particular, any change to the analytics commitments in Section 5. If you don't have an account, we encourage checking this page periodically if you have concerns.

9. Contact

To delete what is stored about your portfolio, you do not have to ask us. Sign in, and Delete my data is on the upload screen, in the small print beside “What we send and keep”. It removes every saved portfolio, everything you entered in the retirement planner, and the record that groups them — from our server and from this browser, immediately, with no review step and no way back. It tells you how many stored portfolios it removed, so you are not taking our word for it. If you hold portfolios for clients, it removes every client’s, not just the one on screen; to remove one client and keep the rest, use Delete in the client list (Section 4e).

Two things it does not touch, named here rather than left to be discovered. Your email address and sign-in are a separate record and survive it, so you can still sign in — to an account with nothing in it. And the fund descriptions in Section 4b are stored with no identifier beside them, which is the same reason there is nothing in them to match to you.

This is an independent project run by one person, and there is no staffed support desk. For a privacy question, or to have your email address itself removed, reply to any email you have received from XRay My Portfolio.

This privacy policy describes the practices of the XRay My Portfolio website and tool only. It does not apply to Fidelity Investments, Charles Schwab, Finnhub, Cloudflare, Resend, or any other third-party service you may interact with separately. We are not affiliated with Fidelity Investments or Charles Schwab.