Track a Fund's Quarterly Holdings
Export SEC fund holdings and insider trades to a CSV and chart top positions.
Introduction
30 Second Summary
Quarterly filings can reveal where a major fund placed its biggest bets. Finding those positions on a government website can take more digging than the insight itself.
In this project, you will use Firecrawl to pull company profiles, quarterly fund holdings, and recent insider trades from SEC filings. Your finished report pairs spreadsheet-ready data with a chart of the fund's ten largest positions.
What You'll Build
Run your script for a fund to see its latest reported positions in a spreadsheet, inspect recent insider activity, and open a bar chart of its largest holdings.
By the end of this project, you'll have:
- A reusable SEC research workflow that pulls a company profile, a fund's latest 13F holdings, and recent insider Form 4 activity.
- Spreadsheet-ready CSV exports that let you review share counts, position values, and insider trades as tables.
- A top-ten holdings chart you can compare with a plain-English answer from an AI assistant connected through the Firecrawl MCP server.
- Secret Mission: An optional challenge to push your SEC research workflow further.
Are there any prerequisites?
You need a Firecrawl account with an API key. You also need curl plus either Python or Node installed.
Before We Start
Raw SEC filing data becomes useful once you can run repeatable searches. Missing tools or an inaccessible account would interrupt that process.
This step prepares your Firecrawl account without configuring project credentials. You will also confirm your local tools. Then you will choose the fund and company that guide the project.
In this step, get ready to:
- Create a Firecrawl account for the upcoming data requests.
- Confirm that your terminal can run Python version 3.
- Choose the fund and company for your filing research.
Create your Firecrawl account
Firecrawl provides the filing data used throughout this project. Your account gives you access to its dashboard before you configure an API key in the next step.
- Visit the official Firecrawl app.
- Create your account by following the on-screen prompts.
- Complete any account verification request that appears.
- Continue until the Firecrawl dashboard opens.
- Confirm that the dashboard includes an API Keys section.
- Leave your API key untouched for now.
That first dependency is sorted. Your Firecrawl account is ready for project configuration in the next step.
Dashboard not loading?
Complete any pending account verification request. Reload the Firecrawl app after verification finishes.
If access still fails, help me troubleshoot my Firecrawl account.
Check your local tools
The project script runs with Python version 3. Your terminal gives you a place to run that script.
Your code editor holds the script. Your spreadsheet application displays the exported CSV data as a table.
macOS
- Press Cmd+Space to open system search.
- Type Terminal into the search field.
- Press Return to open Terminal.
- Check your installed Python version by running this command:
python3 --version
What does this command do?
The python3 command selects Python version 3. The --version option prints the installed version before exiting.
Windows
- Press the Windows key to open system search.
- Type Command Prompt into the search field.
- Press Enter to open Command Prompt.
- Check your installed Python version by running this command:
py -3 --version
What does this command do?
The py -3 command selects an installed Python version 3 interpreter. The --version option prints its version before exiting.
✔️ I see Python 3
Your output begins with Python 3. Your terminal can run the project script.
ⓧ I see an older version
Your terminal found Python. The installed major version cannot run this project.
macOS
- Follow the official Python guide for macOS to install Python version 3.
- Complete the installer by following its on-screen prompts.
- Return to Terminal after the installation finishes.
- Check the new installation by running this command:
python3 --version
What does this command do?
This check asks the active python3 interpreter to report its version. Output beginning with Python 3 confirms that Terminal can use the required major version.
Still seeing the older version?
Restart Terminal so it reloads the available command paths.
If the older version remains active, help me identify the Python installation macOS is using.
Windows
- Follow the official Python guide for Windows to install Python version 3.
- Complete the installer by following its on-screen prompts.
- Return to Command Prompt after the installation finishes.
- Check the new installation by running this command:
py -3 --version
What does this command do?
This check asks the Python launcher to report its Python version 3 interpreter. Output beginning with Python 3 confirms that Command Prompt can use the required major version.
Still seeing the older version?
Restart Command Prompt so it reloads the available command paths.
If the older version remains active, help me identify the Python installation Windows is using.
ⓧ Command not found
Your terminal cannot find a Python version 3 installation. Installing it adds the interpreter required by the project.
macOS
- Follow the official Python guide for macOS to install Python version 3.
- Complete the installer by following its on-screen prompts.
- Return to Terminal after the installation finishes.
- Confirm the installation by running this command:
python3 --version
What does this command do?
The command asks the installed python3 interpreter to print its version. Output beginning with Python 3 confirms that Terminal can now find it.
Still cannot find Python?
Restart Terminal after the installation finishes. This reloads the available command paths.
If the command still fails, help me troubleshoot Python on macOS.
Windows
- Follow the official Python guide for Windows to install Python version 3.
- Complete the installer by following its on-screen prompts.
- Return to Command Prompt after the installation finishes.
- Confirm the installation by running this command:
py -3 --version
What does this command do?
The command asks the Python launcher to report its Python version 3 interpreter. Output beginning with Python 3 confirms that Command Prompt can now find it.
Still cannot find Python?
Restart Command Prompt after the installation finishes. This reloads the available command paths.
If the command still fails, help me troubleshoot Python on Windows.
The remaining applications support the work around your script. Confirm that you can reach both of them before moving on.
- Press Cmd+Space on macOS or the Windows key on Windows to open system search.
- Type the name of your code editor into the search field.
- Press Return on macOS or Enter on Windows to open the editor.
- Return to system search.
- Type the name of your spreadsheet application into the search field.
- Press Return on macOS or Enter on Windows to open the spreadsheet application.
You should now have a terminal that reports Python version 3. You should also have a code editor plus a spreadsheet application available.
Missing a local application?
Install a code editor or spreadsheet application before continuing. Use the official download page for the application you choose.
If you need help choosing one for your operating system, help me compare suitable local applications.
Choose your research targets
A 13F filing gives you a fund's quarterly portfolio positions. A public company gives you the focus for recent insider-trade research.
- Choose Berkshire Hathaway Inc. as your 13F fund.
- Choose Apple Inc. as your company for insider-trade research.
How will you use each target?
Berkshire Hathaway Inc. is the target for quarterly 13F holdings. Those results provide the portfolio positions that you later export.
Apple Inc. is the target for recent insider trades. Those results provide the buys and sells that you investigate later.
Before the final check, which target should be connected to quarterly portfolio positions?
- Review the research roles in the answer you entered.
You should see Berkshire Hathaway Inc. connected to 13F holdings. You should see Apple Inc. connected to insider trades.
Your account is accessible. Your local tools are ready. Next, you will configure Firecrawl before retrieving Apple Inc.'s company profile.
Get Your API Key and Company Profile
Public company filings live in SEC EDGAR. Its raw records can be difficult to search directly.
Your Firecrawl API key authorizes access to its SEC data provider. An Apple company-profile request proves that your connection works.
In this step, get ready to:
- Access your Firecrawl API key from the dashboard.
- Configure the key for your current terminal session.
- Retrieve Apple Inc.'s company profile as JSON.
Copy your Firecrawl API key
An API key acts like a credential for your Firecrawl account. Anyone with the key could use your account's credits.
Credentials can feel risky to handle. You keep this key out of screenshots plus project files throughout the project.
- Return to your account through the Firecrawl dashboard.
- Sign in using the method linked to your existing account.
You'll see the Firecrawl dashboard once authentication succeeds.
- Select API Keys from the dashboard navigation.
- Select Create New API Key if the page has no active key.
- Enter SEC tracker as the key name if Firecrawl asks for one.
- Copy one active API key to your clipboard.
That's the sensitive part handled: your Firecrawl credential is copied. Your project files still contain no secret.
Configure the terminal session
An environment variable gives terminal commands access to a value without adding it to your project files. This variable lasts only for the current terminal session.
- Switch back to the terminal from earlier.
- Replace fc-YOUR-API-KEY in the command below with the key you copied.
- Make the key available to later requests by running this command:
export FIRECRAWL_API_KEY=fc-YOUR-API-KEY
What does this command do?
- The export command makes a value available to commands launched from this terminal.
- The FIRECRAWL_API_KEY variable holds the credential that the sample request reads.
- Closing this terminal session clears the variable.
Your current terminal session now carries the Firecrawl credential. The sample request provides the visible proof that it works.
Retrieve Apple's company profile
A Central Index Key (CIK) identifies a company in SEC records. The profile request uses Apple's CIK to retrieve its filer identity.
Before you run this, do you expect the response to identify Apple from the supplied CIK?
- Ask the filings/companies_get capability for CIK 320193 by running this command:
curl https://api.firecrawl.dev/v2/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"alexandria": {
"provider": "sec-gov",
"capability": "filings/companies_get",
"options": {
"cik": "320193"
}
}
}'
What does this request do?
- The Authorization header reads the API key from your current terminal session.
- The sec-gov provider supplies SEC filing data.
- The filings/companies_get capability retrieves one company identity record.
- The cik option targets Apple's identifier 320193.
The terminal prints a JSON response. Inside data, the record object contains the company profile.
Its name is Apple Inc.. Its cik is 0000320193.
That confirms the full request path works. Your terminal authenticated with Firecrawl before retrieving Apple's SEC identity.
Profile request not working?
- Set the environment variable again if the response mentions an authentication problem.
- Confirm that you ran the request in the same terminal session where you exported the key.
- Confirm that the request uses CIK 320193.
Still stuck? Help me troubleshoot my Firecrawl SEC company-profile request.
- Position the terminal so the API key export command is outside the screenshot area.
Your Firecrawl connection now returns verified SEC company data. Next, you'll use the same connection to retrieve Berkshire Hathaway Inc.'s latest 13F holdings.
Retrieve Fund Holdings
In the last step, Firecrawl returned Apple's SEC EDGAR company profile. That result proved your authenticated request can reach public filing data.
Raw EDGAR filings can be cumbersome to search. In this step, you'll retrieve Berkshire Hathaway's latest available quarterly positions through one structured request.
In this step, get ready to:
- Prepare your current terminal session for a fund holdings request.
- Call Firecrawl's SEC holdings capability for Berkshire Hathaway Inc.
- Verify that the response includes issuer names, share counts, and reported values.
Understand the latest filing
A 13F filing reports an institutional investment manager's positions for a quarter. The latest available filing gives you the most recent holdings that the manager has publicly reported.
What does latest available mean?
The holdings capability selects the latest available 13F-HR filing when the request does not include a report date or accession number. The filing period can trail the current quarter because the response follows the latest public filing.
- Switch back to the terminal from the previous step.
- Keep the current terminal session active so FIRECRAWL_API_KEY remains available.
Your API key stays outside the request body. The authorization header reads it from the current terminal environment.
Retrieve Berkshire Hathaway's holdings
The request identifies Berkshire Hathaway by name. Firecrawl resolves that manager before retrieving the position records from its latest available 13F-HR filing.
This request uses 5 Firecrawl credits. One call gives you the complete response needed for this step.
Before you run the request, do you expect the response to contain one company summary or a list of individual positions?
- Retrieve Berkshire Hathaway's latest available holdings by running this request:
curl https://api.firecrawl.dev/v2/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"alexandria": {
"provider": "sec-gov",
"capability": "filings/holdings_13f",
"options": {
"name": "Berkshire Hathaway Inc."
}
}
}'
What does this request do?
- The Authorization header reads your Firecrawl API key from the terminal environment.
- The sec-gov provider connects the request to SEC filing data.
- The filings/holdings_13f capability retrieves reported investment positions.
- The name option selects Berkshire Hathaway Inc. as the institutional investment manager.
- Locate success near the beginning of the JSON response.
- Confirm that success has the value true.
- Locate report_date inside the filing details.
- Locate the holdings array inside the returned data.
You'll see filing details followed by a list of position objects. This confirms that Firecrawl found Berkshire Hathaway's latest available quarterly filing.
Holdings response missing?
- Return to the same terminal session where you configured FIRECRAWL_API_KEY if the request cannot authenticate.
- Check that the request uses Berkshire Hathaway Inc. as the name option if no holdings are returned.
Still stuck? Help me troubleshoot my Firecrawl SEC holdings request without asking me to reveal my API key.
Verify the position records
Each object in holdings represents a reported position. Three fields provide the core data that your CSV and chart use later.
Before you inspect one position, do you expect its share count and reported value to use the same field?
- Choose one object inside the holdings array.
- Locate the issuer field in that object.
- Locate the shares_or_principal field in the same object.
- Locate the value field in the same object.
You should see a company name in issuer. You should see a numeric share count in shares_or_principal.
You should also see a numeric reported value in value. Together, these fields confirm that the response contains usable position records.
That is the raw dataset secured. Firecrawl has returned Berkshire Hathaway's latest available positions with the fields your analysis needs.
Next, you'll turn these position records into a CSV that opens as a table in your spreadsheet application.
Export Holdings to CSV
Your Firecrawl SEC request already returned Berkshire Hathaway's latest 13F holdings. Each position includes an issuer name, a share count, and a reported value.
Raw JSON is awkward to sort in a spreadsheet. In this step, you'll build a reusable Python script that converts those positions into a CSV table.
In this step, get ready to:
- Retrieve Berkshire Hathaway's latest holdings with a Python script.
- Normalize each position into three spreadsheet-friendly fields.
- Verify the exported table in your spreadsheet application.
Build the holdings request
The script reads FIRECRAWL_API_KEY from your current terminal environment. Your secret stays outside sec_tracker.py while the script requests the latest available filing.
- In the code editor from earlier, create sec_tracker.py on your Desktop by pasting the code below into a new file:
import csv
import json
import os
import urllib.request
def fetch_holdings():
# Read the secret from the environment so it never enters this file.
api_key = os.environ["FIRECRAWL_API_KEY"]
payload = {
"alexandria": {
"provider": "sec-gov",
"capability": "filings/holdings_13f",
"options": {"cik": "0001067983", "limit": 1000},
}
}
request = urllib.request.Request(
"https://api.firecrawl.dev/v2/scrape",
data=json.dumps(payload).encode("utf-8"),
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request) as response:
result = json.load(response)
return result["data"]["holdings"]
if __name__ == "__main__":
# Fetch the latest positions when this file runs directly.
holdings = fetch_holdings()
print(f"Retrieved {len(holdings)} positions.")
What does this code do?
- The standard-library imports handle the request, the JSON response, and the later CSV export.
- The environment lookup keeps your Firecrawl API key out of the project file.
- The request selects the sec-gov provider with the filings/holdings_13f capability.
- The CIK value identifies Berkshire Hathaway Inc.
- The function returns the response's holdings list for later processing.
- Save sec_tracker.py.
- Switch back to the terminal from earlier.
Before you run the script, predict whether it can count the holdings while keeping your API key out of the file.
- Retrieve the latest positions by running this command:
python3 ~/Desktop/sec_tracker.py
What should you see?
Your terminal prints a line beginning with Retrieved. The position count reflects Berkshire Hathaway's latest available filing.
That count confirms your script can retrieve the holdings on demand. The same response data is now ready for normalization.
Script could not retrieve the positions?
- Confirm you are using the terminal session where FIRECRAWL_API_KEY remains configured.
- Check that the CIK in sec_tracker.py is 0001067983.
- Check your internet connection if the request cannot reach Firecrawl.
Still stuck? Help me debug my Firecrawl holdings request.
Normalize and export the positions
Every holding contains more fields than the spreadsheet needs. Normalization keeps only the issuer, share count, and reported value so every row follows the same shape.
- In sec_tracker.py, locate the current entry-point block:
if __name__ == "__main__":
# Fetch the latest positions when this file runs directly.
holdings = fetch_holdings()
print(f"Retrieved {len(holdings)} positions.")
What does this block do now?
This block currently retrieves the positions and prints their count. It is the point where the export workflow begins.
- Replace that entry-point block with the expanded version below:
if __name__ == "__main__":
# Fetch the latest positions when this file runs directly.
holdings = fetch_holdings()
print(f"Retrieved {len(holdings)} positions.")
# Keep the three fields needed for spreadsheet analysis.
fieldnames = ["issuer", "share_count", "reported_value"]
rows = [
{
"issuer": position["issuer"],
"share_count": position["shares_or_principal"],
"reported_value": position["value"],
}
for position in holdings
]
# Write the table beside this script for easy access.
output_path = os.path.join(os.path.dirname(__file__), "berkshire_holdings.csv")
with open(output_path, "w", newline="", encoding="utf-8") as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
print(f"Wrote {len(rows)} positions to berkshire_holdings.csv.")
How does the export work?
- The list comprehension maps Firecrawl's field names to consistent CSV column names.
- The output_path value places the CSV beside sec_tracker.py on your Desktop.
- The DictWriter writes the column header before writing each normalized position.
- The final message reports how many rows reached the file.
- Save sec_tracker.py.
Before you rerun the script, predict which three column names the new CSV will contain.
- Create the normalized CSV by running the script again:
python3 ~/Desktop/sec_tracker.py
What should you see now?
Your terminal first reports the retrieved position count. A second line begins with Wrote and confirms the creation of berkshire_holdings.csv.
The CSV now sits on your Desktop beside the script. Each API position has one normalized row.
Your holdings data is now portable. You can sort it, filter it, or reuse it without digging through the original response.
CSV file missing?
- Confirm you saved the expanded entry-point block in sec_tracker.py.
- Check the terminal for an error before the line that starts with Wrote.
- Look for berkshire_holdings.csv on your Desktop beside the script.
Need another pair of eyes? Help me find why my CSV export was not created.
✔️ Awesome, I've got everything!
Great. Confirm that you saved sec_tracker.py before opening the CSV.
ⓧ I'd like to double check the full code
import csv
import json
import os
import urllib.request
def fetch_holdings():
# Read the secret from the environment so it never enters this file.
api_key = os.environ["FIRECRAWL_API_KEY"]
payload = {
"alexandria": {
"provider": "sec-gov",
"capability": "filings/holdings_13f",
"options": {"cik": "0001067983", "limit": 1000},
}
}
request = urllib.request.Request(
"https://api.firecrawl.dev/v2/scrape",
data=json.dumps(payload).encode("utf-8"),
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request) as response:
result = json.load(response)
return result["data"]["holdings"]
if __name__ == "__main__":
# Fetch the latest positions when this file runs directly.
holdings = fetch_holdings()
print(f"Retrieved {len(holdings)} positions.")
# Keep the three fields needed for spreadsheet analysis.
fieldnames = ["issuer", "share_count", "reported_value"]
rows = [
{
"issuer": position["issuer"],
"share_count": position["shares_or_principal"],
"reported_value": position["value"],
}
for position in holdings
]
# Write the table beside this script for easy access.
output_path = os.path.join(os.path.dirname(__file__), "berkshire_holdings.csv")
with open(output_path, "w", newline="", encoding="utf-8") as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
print(f"Wrote {len(rows)} positions to berkshire_holdings.csv.")
Open the holdings table
A CSV proves its value when each field opens in a separate spreadsheet column. This final check confirms the export is ready for analysis.
Before you open the file, predict how the issuer names, share counts, and reported values will line up.
- Switch to Finder on macOS or File Explorer on Windows.
- Select Desktop in the sidebar.
- Double-click berkshire_holdings.csv.
Seeing an import screen?
Some spreadsheet applications show an import screen before opening a CSV. Keep the detected comma separator so each field receives its own column.
You should see a table headed issuer, share_count, and reported_value. The rows underneath contain Berkshire Hathaway's latest available reported positions.
You have turned an API response into a spreadsheet-ready holdings table. The file is now easy to inspect or share.
Values appearing in one column?
Reopen the file through your spreadsheet application's import flow. Choose a comma as the field separator so the three headers split into separate columns.
Still seeing one column? Help me import my holdings CSV correctly.
Your reusable tracker now retrieves a fund's holdings and exports a clean table. Next, you'll collect recent insider buys and sells for Apple Inc.
Find Recent Insider Trades
Your script already retrieves Berkshire Hathaway's latest holdings. It turns those positions into a spreadsheet-ready table.
That view covers a fund's portfolio at quarter end. It does not show what Apple insiders did more recently. In this step, you'll use Firecrawl's SEC EDGAR filings search to find Apple's recent Form 4 records.
In this step, get ready to:
- Search for recent Apple Inc. Form 4 filings.
- Normalize insider purchases or sales into a second CSV export.
- Verify that both spreadsheet-ready exports still work.
Search Apple's Form 4 filings
The Apple company profile from earlier gives your script the company identity needed to target one filer. A Form 4 search narrows the results to insider transaction filings.
What does the search return?
- The provider remains sec-gov.
- The capability is filings/search.
- The company filter uses the Apple CIK returned by the profile request from earlier.
- The forms filter keeps Form 4 records.
- The returned hits include fields such as form, filing_date, and document_url.
- Return to sec_tracker.py in the code editor from earlier.
- Add a second Firecrawl request below the existing Berkshire export using the search shape above.
- Save sec_tracker.py.
Before you run the updated script, do you expect the Form 4 results to describe Berkshire Hathaway or Apple Inc.?
- Run sec_tracker.py with Python from the terminal panel from earlier.
You'll see recent Apple Inc. Form 4 results. Each result identifies the filing and its date.
Export the insider trade table
Filing metadata locates each disclosure. The matched Form 4 document supplies the insider activity needed to distinguish purchases from sales.
How should each trade be normalized?
- The insider activity field identifies the person or reported activity.
- The transaction type labels the record as a purchase or sale.
- The date field keeps the transaction date when available.
- The filing date provides the fallback date for the disclosure.
- Extend sec_tracker.py to normalize the matched Form 4 records into the fields above.
- Write the normalized records to apple_insider_trades.csv while preserving the existing Berkshire export.
- Save sec_tracker.py.
Before you rerun the script, do you expect one CSV file or two CSV files to remain after it finishes?
- Run sec_tracker.py with Python from the terminal panel from earlier.
- Check the code editor's file browser for the new export.
You'll see apple_insider_trades.csv beside berkshire_holdings.csv. That is the second export working: your tracker now covers fund positions plus insider activity.
Insider trade export empty?
- Confirm the request uses the Apple CIK returned by the company profile from earlier.
- Confirm the forms filter keeps Form 4 records.
- Confirm FIRECRAWL_API_KEY remains configured in the current terminal session.
Still stuck? Help me debug an empty Apple Form 4 export from sec_tracker.py.
Verify both CSV exports
The final check confirms that the new insider workflow did not replace the holdings workflow. Both tables should remain available for the next analysis step.
Before you check the new file, which columns should make an insider purchase or sale easy to spot?
- Switch back to the spreadsheet application from earlier.
- Load apple_insider_trades.csv through the spreadsheet application's file picker.
You'll see a table of recent Apple insider activity. Each row shows a transaction type plus a filing or transaction date.
- Return to the existing berkshire_holdings.csv table.
You'll still see Berkshire position issuers with share counts and reported values. Your two exports now answer two different questions from the same script.
Your tracker now captures Berkshire Hathaway's portfolio plus recent Apple insider trades. Next, you'll turn the fund's largest positions into a top-ten chart.
Chart the Top Ten Positions
Your tracker already turns Firecrawl SEC data into two spreadsheet-ready files. You can inspect Berkshire Hathaway's positions beside Apple's recent insider activity.
A long holdings table hides which positions dominate the portfolio. This step ranks the records by reported value before saving the ten largest positions as an image.
In this step, get ready to:
- Add Matplotlib to the current Python environment.
- Rank Berkshire Hathaway's holdings by reported value.
- Save the ten highest-value positions to top_ten_positions.png.
Add charting support
Matplotlib turns numeric data into charts that Python can save as image files. A horizontal layout leaves enough room for long issuer names.
- Switch back to the terminal from earlier.
- Add Matplotlib to the Python environment by running this command:
python -m pip install -U matplotlib
What does this command do?
The command uses pip through the same Python interpreter that runs your tracker. It installs Matplotlib plus the packages Matplotlib needs.
The first installation can take a minute while the packages download. A quiet pause during this command is normal.
- Confirm that the terminal returns to its prompt without an installation error.
Your Python environment can now turn the holdings data into an image.
Matplotlib did not install?
Make sure you ran the command in the same terminal environment that runs sec_tracker.py. A different Python environment may have its own package installation.
If the installation still fails, help me troubleshoot the Matplotlib installation.
Add the ranking and chart logic
The existing CSV file already contains the issuer names plus reported values. Your new function reads those rows before selecting the ten largest positions.
- Switch back to the code editor from earlier.
- In sec_tracker.py, scroll to the bottom of the file.
- Paste the charting code below after the existing CSV export logic:
import csv
import matplotlib.pyplot as plt
def create_top_ten_chart(csv_path, chart_path):
# Load the normalized holdings exported earlier
with open(csv_path, newline="", encoding="utf-8") as csv_file:
holdings = list(csv.DictReader(csv_file))
# Rank positions by reported value before keeping the largest ten
top_ten = sorted(
holdings,
key=lambda holding: float(holding["reported_value"]),
reverse=True,
)[:10]
# Reverse the display order so the largest bar appears at the top
issuers = [holding["issuer"] for holding in reversed(top_ten)]
values = [float(holding["reported_value"]) for holding in reversed(top_ten)]
# Build a readable horizontal chart and save it as a PNG image
fig, ax = plt.subplots(figsize=(10, 6))
ax.barh(issuers, values)
ax.set_xlabel("Reported value")
ax.set_title("Berkshire Hathaway Top Ten Positions")
fig.tight_layout()
fig.savefig(chart_path, dpi=150, bbox_inches="tight")
plt.close(fig)
# Generate the chart from the latest normalized holdings export
create_top_ten_chart("berkshire_holdings.csv", "top_ten_positions.png")
print("Saved top_ten_positions.png")
What does this code do?
- The csv.DictReader call loads each holding using the column names from berkshire_holdings.csv.
- The sorted() call ranks every position by reported_value.
- The [:10] slice keeps only the ten largest positions.
- The barh() call creates horizontal bars for the issuer names.
- The savefig() call writes the completed figure to top_ten_positions.png.
- Save sec_tracker.py.
Before you run the tracker, which Berkshire Hathaway holding do you expect to take the top spot?
- Switch back to the terminal from earlier.
- Generate refreshed data files plus the chart by running this command:
python sec_tracker.py
What should you see?
Your existing export confirmations should appear first. The final confirmation should read Saved top_ten_positions.png.
The script also creates top_ten_positions.png beside sec_tracker.py.
Chart file missing?
Check that berkshire_holdings.csv is beside sec_tracker.py. The chart function reads that file from the current folder.
Check that the holdings file contains issuer plus reported_value columns. Those names must match the normalized export.
If the script reports another problem, help me debug the chart generation.
Inspect the saved chart
The saved PNG gives you a visual ranking that is faster to scan than the full holdings table. The longest bar represents the highest reported value.
Before you open the image, which issuer do you expect to have the longest bar?
- In the code editor's Explorer sidebar, select top_ten_positions.png.
You'll see ten horizontal bars with issuer labels. The highest-value position sits at the top with the longest bar.
You have turned Berkshire Hathaway's holdings into a chart you can regenerate after every new quarterly filing.
Your tracker now produces tables plus a visual summary from the same filing data. Next, you'll compare these numbers with an answer requested in plain English.
Ask the SEC Data Assistant
Your script has already turned Berkshire Hathaway's latest holdings into berkshire_holdings.csv and top_ten_positions.png. Those files give you a precise reference for checking another retrieval method.
Model Context Protocol (MCP) lets an AI assistant call Firecrawl as a tool. This gives you a plain-English route to the same SEC records for comparison with your script.
In this step, get ready to:
- Connect Claude to the Firecrawl MCP server.
- Ask for Berkshire Hathaway's latest 13F holdings in plain English.
- Compare the assistant's response with your existing CSV and chart results.
Connect Claude to Firecrawl
Claude's native Firecrawl connector creates an authenticated MCP connection through browser approval. That connection unlocks Alexandria data tools for your selected Firecrawl team.
Connecting an account can feel sensitive. Claude completes the authorization in your browser, so your FIRECRAWL_API_KEY stays in the terminal instead of the conversation.
- Open the official Firecrawl connector guide.
- Select Add to Claude.
- Use your existing Firecrawl session when Claude opens the authorization page.
- Choose the Firecrawl team used for this project.
- Approve the connection.
Connection complete. Claude can now request authenticated SEC data through your Firecrawl team without exposing your API key.
- Start a new Claude conversation.
- Open the + menu beside the conversation input.
- Select Connectors.
- Enable Firecrawl.
You should see Firecrawl enabled for the conversation. This confirms that the assistant can access the MCP connection.
Firecrawl connector missing?
- Reload Claude after approving the Firecrawl connection.
- Confirm that you approved access for the Firecrawl team used in this project.
- Ask your organization Owner to allow the connector if you use a managed Team or Enterprise account.
Still cannot enable the connector? Help me troubleshoot my Firecrawl connector in Claude.
Ask about Berkshire Hathaway's holdings
The assistant needs the same scope that your script used. The SEC provider call costs 5 Firecrawl credits.
- Prepare a plain-English request for the same holdings data by copying this prompt:
Use Firecrawl's SEC data provider to retrieve Berkshire Hathaway Inc.'s latest available 13F holdings. Rank the position records by reported value from highest to lowest. Return the top ten with the issuer name, share count, reported value, and filing report date. Cite the filing used.
What does this prompt do?
- The first sentence directs Claude to use Firecrawl's SEC data instead of answering from general knowledge.
- The ranking instruction matches the reported-value order used for your chart.
- The requested fields give you issuer names, share counts, and reported values for a row-by-row comparison.
- The filing date helps identify which quarterly report produced the answer.
Before you submit it, which issuer do you expect to rank first? Keep that name in mind.
Claude may ask for permission before using Firecrawl. That confirmation keeps the external data request visible to you.
- Submit the prompt in your Claude conversation.
- Approve the Firecrawl tool request if Claude asks for permission.
You should receive a plain-English answer containing ten ranked position records. Each record should include its issuer, share count, and reported value.
That is the conversational route working. Your assistant has retrieved the SEC holdings through Firecrawl without running sec_tracker.py.
No SEC holdings in the response?
- Confirm that Firecrawl remains enabled in the conversation's connector menu.
- Start a new Claude conversation after changing the connector connection.
- Repeat the prompt with its instruction to use Firecrawl's SEC data provider.
Need help with the retrieval? Help me troubleshoot why Claude is not retrieving Berkshire Hathaway's 13F holdings through Firecrawl.
Compare the answer with your script
A 13F reported value comes from the filing's quarter-end position record. Use that field when comparing the assistant's answer with your local results.
Before you compare, do you expect the assistant's first three position records to match your chart? Hold that prediction while you check.
- Keep the Claude response visible.
- Open berkshire_holdings.csv in your spreadsheet application.
- Open top_ten_positions.png in your image viewer.
- Find the assistant's first-ranked issuer in berkshire_holdings.csv.
- Compare its share count with the assistant's share count.
- Compare its reported value with the assistant's reported value.
- Repeat the comparison for the next two ranked position records.
- Check the assistant's full ranking against the bar order in top_ten_positions.png.
You should find the same issuers and reported values across the assistant response, spreadsheet, and chart. The order should follow reported value from highest to lowest.
You now have two verified routes to the same SEC holdings. Your script creates reusable files, while the MCP assistant answers the question conversationally.
Numbers do not match?
- Confirm that the assistant used the latest available 13F filing for Berkshire Hathaway Inc.
- Confirm that the assistant ranked reported values from the filing.
- Ask the assistant to preserve every position record as filed if it grouped records with the same issuer.
- Return to the earlier script step if the assistant cites a newer filing than the one used for your local files.
Still seeing a mismatch? Help me compare my Firecrawl MCP response with my Berkshire holdings CSV and chart.
Secret mission
Audit the AI Ranking Against the Data
Ask the connected assistant to rank Berkshire Hathaway Inc.'s three highest-value positions. Then verify every claim against the existing CSV and chart before writing a source-backed conclusion.
Clean Up Your Resources
Clean Up Your Resources
Your local files have no ongoing cost. Choose whether to keep the tracker ready, pause your work, or delete its files before another Firecrawl call uses 5 credits.
Resources you used:
- The reusable sec_tracker.py script.
- The berkshire_holdings.csv holdings export.
- The apple_insider_trades.csv insider-trade export.
- The top_ten_positions.png chart.
- The FIRECRAWL_API_KEY value held in the current terminal session.
Keep everything running
No action needed. Choose this if you want to rerun the tracker when a new quarter becomes available.
- Keep sec_tracker.py for future holdings and insider-trade research.
- Keep berkshire_holdings.csv for quarter-to-quarter comparisons.
- Keep apple_insider_trades.csv as a record of the retrieved insider activity.
- Keep top_ten_positions.png as a visual snapshot of Berkshire Hathaway Inc.'s ten highest-value reported positions.
- Review your available credit balance in the Firecrawl dashboard before each rerun.
Pause - I'll come back to this later
Pausing clears the session-only API key while preserving the script and its outputs. Your research remains available when you return.
- Close the current terminal window to clear its session-only FIRECRAWL_API_KEY value.
- Keep sec_tracker.py with its three generated output files in their current location.
- Reconfigure FIRECRAWL_API_KEY using the same method from earlier when you resume.
Delete - I don't want to use this again
This option removes the local script and its generated outputs. Your Firecrawl account remains available.
Deletion is permanent
The script, both CSV exports, and the chart disappear from your computer. Only files with another copy remain available.
macOS
- Switch back to the terminal session where you ran sec_tracker.py.
- Confirm that the terminal is in the folder containing your project files by running this command:
ls
What does this check?
This lists the files in the terminal's current folder. You should see sec_tracker.py, berkshire_holdings.csv, apple_insider_trades.csv, and top_ten_positions.png before deleting them.
- Delete the script and generated files by running this command:
rm sec_tracker.py berkshire_holdings.csv apple_insider_trades.csv top_ten_positions.png
What does this command do?
This removes the Python script, both CSV exports, and the chart image from the current folder.
- Verify that the project files are gone by running this command:
ls
What should I see?
You should no longer see any of the four project filenames. This confirms that the local artifacts have been removed.
Still see a project file?
- Check that each filename matches the deletion command exactly.
- Return to the terminal session where you ran the script.
Help me remove the remaining local project files.
- Close the current terminal window to clear its session-only FIRECRAWL_API_KEY value.
Windows
- Switch back to the terminal session where you ran sec_tracker.py.
- Confirm that the terminal is in the folder containing your project files by running this command:
dir
What does this check?
This lists the files in the terminal's current folder. You should see sec_tracker.py, berkshire_holdings.csv, apple_insider_trades.csv, and top_ten_positions.png before deleting them.
- Delete the script and generated files by running these commands:
del sec_tracker.py
del berkshire_holdings.csv
del apple_insider_trades.csv
del top_ten_positions.png
What do these commands do?
These commands remove the Python script, both CSV exports, and the chart image from the current folder.
- Verify that the project files are gone by running this command:
dir
What should I see?
You should no longer see any of the four project filenames. This confirms that the local artifacts have been removed.
Still see a project file?
- Check that each filename matches the deletion commands exactly.
- Return to the terminal session where you ran the script.
Help me remove the remaining local project files.
- Close the current terminal window to clear its session-only FIRECRAWL_API_KEY value.
Linux
- Switch back to the terminal session where you ran sec_tracker.py.
- Confirm that the terminal is in the folder containing your project files by running this command:
ls
What does this check?
This lists the files in the terminal's current folder. You should see sec_tracker.py, berkshire_holdings.csv, apple_insider_trades.csv, and top_ten_positions.png before deleting them.
- Delete the script and generated files by running this command:
rm sec_tracker.py berkshire_holdings.csv apple_insider_trades.csv top_ten_positions.png
What does this command do?
This removes the Python script, both CSV exports, and the chart image from the current folder.
- Verify that the project files are gone by running this command:
ls
What should I see?
You should no longer see any of the four project filenames. This confirms that the local artifacts have been removed.
Still see a project file?
- Check that each filename matches the deletion command exactly.
- Return to the terminal session where you ran the script.
Help me remove the remaining local project files.
- Close the current terminal window to clear its session-only FIRECRAWL_API_KEY value.
Nice Work!
Nice Work!
You pulled it off! Your reusable SEC tracker now turns Firecrawl data into spreadsheet-ready CSV files plus a chart of Berkshire Hathaway Inc.'s ten highest-value positions.
You've learned how to:
- Research public filings by retrieving Apple Inc.'s company profile. You also retrieved Berkshire Hathaway Inc.'s latest 13F holdings. You found recent Apple Inc. Form 4 filings too.
- Build a reusable data pipeline with sec_tracker.py. It normalizes filing records into berkshire_holdings.csv plus apple_insider_trades.csv. It ranks holdings by reported value. It saves a chart of the ten highest-value positions as top_ten_positions.png.
- Cross-check AI research by comparing a plain-English answer from the Firecrawl MCP server with the script's CSV files plus chart results.
- Secret Mission: Completed the optional Secret Mission to push the SEC research workflow further.
Ready to quiz yourself?