Build an Exa Research Scout with Python

Build a Python CLI that searches the web and saves evidence-linked reports.

Introduction

30 Second Summary

A list of search results tells you where to look. A useful research brief also shows why each source matters.

In this project, you will build a Python command-line research scout powered by the Exa Search API. It will save five source links, publication dates, and evidence highlights in a Markdown report.

What You'll Build

Your finished scout turns a terminal question into a shareable research_report.md brief that places each source beside the evidence that makes it useful.

By the end of this project, you'll have:

  • A live web search you can launch with any natural-language research question.
  • Optional domain filtering that limits results to websites you choose for a given topic.
  • A shareable Markdown brief you can open in Visual Studio Code to review five sources with publication dates and evidence highlights.
  • Secret Mission: Add a publication-date filter that limits time-sensitive research to sources published after a chosen date.

Are there any prerequisites?

You need a Mac with Python 3.9 or newer, Visual Studio Code, an Exa account, an API key, and basic Python experience. Step 1 prepares the isolated project environment with the Exa Python SDK version 2.20.0.

Before We Start

A clear purpose gives your research scout a target before any hands-on work begins. This is your moment to choose the first question and identify who needs the resulting brief.

Your question determines what the scout searches for. Your intended audience shapes which evidence will make the brief useful.

Shape a Useful Starting Question

A focused question names the topic. It also names the angle you want to investigate.

Your audience could be a product team. It could also be a researcher or student who needs evidence linked to its original sources.

Prepare the Exa Python Project

Your research question gives the scout a target. The project still needs a clean workspace with a predictable dependency.

An isolated virtual environment keeps the Exa Python SDK inside this project. A shell-based API key keeps the secret out of your files.

In this step, get ready to:
  • Create the exa-research-scout workspace.
  • Install the pinned Exa SDK inside .venv.
  • Prepare terminal-only API authentication.
Create the project workspace

The requirements.txt file records the exact SDK release for this project. That pin makes the installation repeatable.

  • Press Cmd+Space to open macOS search.
  • Type Visual Studio Code in the search bar. Press Enter to open it.

You will see Visual Studio Code open without a project folder selected.

  • Click File in the top menu bar.
  • Select Open Folder....

You will see the folder picker for your new workspace.

  • Navigate to your Desktop in the folder picker.
  • Create a folder named exa-research-scout on your Desktop.

The folder picker now shows exa-research-scout on your Desktop.

  • Select the exa-research-scout folder.
  • Confirm the folder selection.

You will see exa-research-scout as the open folder in the VS Code file sidebar.

  • Use the new-file control in the VS Code file sidebar to create requirements.txt inside exa-research-scout.
  • Add the pinned dependency to requirements.txt by replacing the file contents with the line below:
exa-py==2.20.0

What does this requirement do?

  • The exa-py name selects the official Python SDK package for Exa.
  • The 2.20.0 pin keeps every installation on the project release.
  • Save requirements.txt.
  • Check the VS Code file sidebar for requirements.txt inside exa-research-scout.

You will see the saved file in the sidebar. Its editor contains the single pinned requirement.

Can't find the requirements file?

  • Check that requirements.txt sits directly inside exa-research-scout.
  • Check that the filename does not contain an extra extension.

Ask for help with the file location: Help me check where requirements.txt was created in my VS Code project.

✔️ Awesome, I've got everything!

Your dependency pin is saved inside the project workspace.

ⓧ I'd like to double check the full code

exa-py==2.20.0

What should the full file contain?

The complete requirements.txt file contains one dependency pin. Compare the line exactly before continuing.

Isolate and install the dependency

A virtual environment gives this folder its own Python package set. Activating .venv directs python to that isolated set.

  • Use the integrated terminal control in VS Code to open a terminal panel.

You will see a terminal panel connected to the open exa-research-scout folder.

  • Create the environment and install the pinned dependency by running these commands:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

What do these commands do?

  • The first command creates the isolated .venv environment inside your project.
  • The second command activates that environment for the current terminal.
  • The third command installs every dependency pinned in requirements.txt.
  • Check that the terminal prompt shows the active environment name.
  • Check the VS Code file sidebar for the new .venv folder.

You will see the environment name at the start of your terminal prompt. You will also see .venv in the project sidebar.

Is the environment missing?

  • Check that the integrated terminal is connected to exa-research-scout.
  • Run the activation command above again if the prompt does not show the environment name.
  • Run the installation command above again if the package download stopped.

Get help with the environment setup: Help me diagnose why my .venv environment or exa-py installation is missing.

Export the API key and verify the SDK

Your Exa API key provides API authentication for future search requests. The shell export keeps the credential in the current terminal session.

Pasting a credential can feel risky. This approach keeps the key out of requirements.txt and future Python files.

Before you run these commands, which package name and version do you expect the inspection to report?

  • Replace your-api-key in the command below with your existing Exa API key.
  • Export the key and inspect the installed SDK by running these commands:
export EXA_API_KEY="your-api-key"
python -m pip show exa-py

What do these commands confirm?

  • The export command makes EXA_API_KEY available in the current terminal session.
  • The inspection command reads the Exa package metadata from the active environment.

The package details should include Name: exa-py and Version: 2.20.0.

✔️ I see the pinned version

That setup is locked in. Your active environment contains exa-py version 2.20.0.

ⓧ I see a different version

A different version means the active environment does not match requirements.txt.

  • Reinstall the pinned requirement by running this command:
python -m pip install -r requirements.txt

What does this command fix?

The command reads the exact package pin from requirements.txt. It installs that release into the active environment.

  • Inspect the installed package again by running this command:
python -m pip show exa-py

What does this check show?

The command displays metadata for the Exa package visible to the active Python interpreter.

You should now see Name: exa-py and Version: 2.20.0.

ⓧ The package is not found

Missing package metadata means the current Python interpreter cannot see the installed SDK.

  • Activate the existing environment and install the requirement by running these commands:
source .venv/bin/activate
python -m pip install -r requirements.txt

What do these commands restore?

The first command reconnects the terminal to .venv. The second command installs the pinned package there.

  • Inspect the installed package by running this command:
python -m pip show exa-py

What does this check prove?

The package metadata proves that the active environment can load the installed Exa SDK.

You should now see Name: exa-py and Version: 2.20.0.

Still missing the package details?

  • Check that requirements.txt contains the exact package pin.
  • Check that the terminal remains connected to the exa-research-scout folder.
  • Keep your API key out of any error message you share.

Get help with the package check: Help me diagnose why pip cannot show exa-py version 2.20.0 in my active .venv environment.

Your isolated workspace is ready for live Exa requests. Next, you will run the scout's first web search.

Run a Basic Exa Search

Your setup can now authenticate with the Exa Search API. That preparation is doing its job because your project is ready to make a live request.

A result list becomes useful when you can judge what each source contributes. In this step, you will build a basic search to discover whether titles and URLs provide enough context.

In this step, get ready to:
  • Create a Python script that collects a non-empty research question.
  • Request five results from Exa without page contents.
  • Run the search to assess the titles and URLs it returns.
Collect a valid research question

A search needs a clear question before it can request useful sources. Your script will stop early when the input is empty.

  • Select the Explorer sidebar in Visual Studio Code.
  • Use the new-file control beside the exa-research-scout folder.
  • Enter research_scout.py as the file name.
  • Confirm the file name to create the empty file.
  • Paste this input section into research_scout.py:
from exa_py import Exa

query = input("Research question: ").strip()
if not query:
    raise SystemExit("A research question is required.")

How does the input check work?

  • The Exa import makes the SDK client available to the script.
  • The input() call collects the research question. The strip() call removes surrounding whitespace.
  • The guard stops the script when query is empty. This prevents a blank search request.
  • Save research_scout.py.
  • Test the empty-input guard in the integrated terminal by running this command:
python research_scout.py

What does this check prove?

This command starts research_scout.py with the Python interpreter from your active virtual environment. The script pauses at its input prompt.

  • Press Enter at the Research question: prompt without typing a question.

You will see A research question is required. because the guard rejected the empty input.

Input check not running?

Confirm that the integrated terminal still shows the active .venv environment. Make sure you saved research_scout.py before running it.

If Python cannot import the SDK, confirm that this is the same terminal where you verified exa-py.

Help me troubleshoot this input check.

Send the basic search request

The validated question can now flow into an Exa client. The response contains result objects that your script can enumerate in a consistent order.

  • Place your cursor below the line containing raise SystemExit("A research question is required.") in research_scout.py.
  • Paste the search flow below that line:
exa = Exa()
response = exa.search(
    query,
    type="auto",
    num_results=5,
    contents=False,
)

for index, result in enumerate(response.results, start=1):
    print(f"{index}. {result.title or 'Untitled source'}")
    print(f"   {result.url}")

How does the search flow work?

  • The Exa() client reads EXA_API_KEY from the current terminal environment. Your key stays outside the Python file.
  • The exa.search() call sends the validated query to Exa. It requests five results with automatic search behavior.
  • The loop walks through response.results from number one. Each pass prints a title followed by its URL.
  • The Untitled source fallback keeps a result readable when its title is missing.
  • Save research_scout.py.

✔️ Awesome, I've got everything!

Your basic search flow is complete. Double-check that you saved research_scout.py.

ⓧ I'd like to double check the full code

from exa_py import Exa

query = input("Research question: ").strip()
if not query:
    raise SystemExit("A research question is required.")

exa = Exa()
response = exa.search(
    query,
    type="auto",
    num_results=5,
    contents=False,
)

for index, result in enumerate(response.results, start=1):
    print(f"{index}. {result.title or 'Untitled source'}")
    print(f"   {result.url}")

Before you run the completed script, consider whether its results will explain why each source matters.

  • Run the completed search in the current integrated terminal by using this command:
python research_scout.py

What does this command start?

This starts the completed script inside your active virtual environment. It pauses at the research question prompt before sending the request.

  • Enter the research question you chose in Step 0 at the Research question: prompt.

You will see five numbered source titles. You will see one URL beneath each title.

Your first live search works. The output stops at titles and links without excerpts that show why each page is relevant.

Why is this shortfall useful?

Titles can suggest relevance without showing the evidence inside a page. This result gives you a concrete retrieval gap to solve.

Search did not complete?

Confirm that you are using the integrated terminal where EXA_API_KEY is still exported. A new terminal session does not inherit that session-only value.

Check your internet connection if the request does not return results. The script needs live access to Exa for each search.

Help me troubleshoot my basic Exa search.

The basic search has exposed the missing evidence layer. Next, you will retrieve query-relevant highlights for each source.

Retrieve Evidence with Highlights

Your scout already returns five Exa sources. Each result stops at a title and URL.

Content highlights close that evidence gap by extracting query-relevant passages. Optional domain filtering gives you control over where the search looks.

In this step, get ready to:
  • Convert optional domain text into a clean list.
  • Request highlights from five Exa results.
  • Compare results with and without a domain filter.
Parse optional domain filters

A comma-separated prompt arrives as one string. parse_domains() turns that string into the list expected by include_domains.

Empty input becomes None. This leaves the search open to all domains.

  • In Visual Studio Code, click research_scout.py in the left file list.
  • Place your cursor below from exa_py import Exa.
  • Add the domain parser by pasting this code:
def parse_domains(raw_domains):
    domains = [
        domain.strip()
        for domain in raw_domains.split(",")
        if domain.strip()
    ]
    return domains or None

What does this function do?

  • The split(",") call separates the input at each comma.
  • The strip() call removes surrounding whitespace from each domain.
  • The condition removes blank entries.
  • The domains or None expression gives the search request a list or None.
Build the evidence search

A dedicated search helper keeps the request settings in one place. It also replaces full page retrieval with concise evidence highlights.

  • Place your cursor below the parse_domains() function.
  • Add the search helper by pasting this code:
def search_web(query, include_domains):
    exa = Exa()
    return exa.search(
        query,
        type="auto",
        num_results=5,
        include_domains=include_domains,
        contents={"highlights": {"max_characters": 1000}},
    )

How does the search retrieve evidence?

  • The Exa() client reads EXA_API_KEY from your current terminal session.
  • The type="auto" setting lets Exa choose the search behavior for the query.
  • The num_results=5 setting limits the response to five sources.
  • The include_domains value carries the optional source restriction.
  • The contents object requests query-relevant highlights with a maximum of 1000 characters.

A regression run confirms that Python can parse both helper functions before continuing to the existing search request.

  • Save research_scout.py.
  • Confirm the existing search still runs by using this command:
python research_scout.py

What does this check prove?

Python loads the new function definitions before it reaches the existing request. A successful search confirms that both helpers are syntactically valid.

  • Enter your research question from Step 0.

You'll see five numbered titles and URLs. Good progress. Your new helpers now sit safely alongside the working search.

Seeing a syntax or indentation problem?

Check that each function body uses four spaces of indentation. Keep the closing parenthesis in search_web() aligned with the return statement.

Ask for help with the exact section that fails: Help me check the indentation in my two Python helper functions.

Run unfiltered and filtered searches

The main flow connects your prompts to the two helpers. It also prints publication metadata before listing the evidence from each result.

  • Replace the current top-level block from query = input("Research question: ").strip() through the final URL print with this main flow:
def main():
    query = input("Research question: ").strip()
    if not query:
        raise SystemExit("A research question is required.")

    raw_domains = input(
        "Optional domains, comma-separated (press Enter for all): "
    ).strip()
    include_domains = parse_domains(raw_domains)

    response = search_web(query, include_domains)

    for index, result in enumerate(response.results, start=1):
        print(f"{index}. {result.title or 'Untitled source'}")
        print(f"   {result.url}")
        print(f"   Published: {result.published_date or 'Date unavailable'}")
        for highlight in result.highlights or ["No highlight returned."]:
            print(f"   Evidence: {highlight}")


if __name__ == "__main__":
    main()

How does the main flow use the results?

  • The first prompt rejects an empty research question.
  • The second prompt sends optional domain text through parse_domains().
  • The search_web() call returns the response object.
  • The outer loop prints the title and URL for each result.
  • The inner loop prints every available highlight as evidence.
  • Save research_scout.py.

Before the first run, form a prediction about the evidence that appears beneath each source.

  • Start an unfiltered search by running this command:
python research_scout.py

What does this run test?

This run sends None as the domain filter. The request can search across all domains while retrieving highlights.

  • Enter your research question from Step 0.
  • Press Enter at the optional domains prompt.

You'll see each returned title and URL followed by a publication date. You'll also see one or more evidence lines when highlights are available.

Missing evidence for a source?

Some pages may not return a highlight. Your script handles that case by printing No highlight returned..

If every source lacks evidence, check that contents uses the nested highlights object shown above. Help me inspect my Exa highlights request.

Before the second run, predict how limiting the domain changes the source list.

  • Start a filtered search by running the script again:
python research_scout.py

What does the second run prove?

This run passes a domain list to include_domains. The returned evidence now comes from the source boundary you selected.

  • Enter the same research question.
  • Enter one relevant domain at the optional domains prompt.

You'll see returned sources from the selected domain. Each source includes a publication date plus an Evidence: line when a highlight is available.

That's the evidence gap closed. Your scout now shows why each source matters within the domains you choose.

No filtered sources returned?

Try a domain that publishes material about your research question. Remove spaces around the domain if the input contains an accidental gap.

Use the unfiltered run to identify a relevant source domain. Help me choose a useful domain filter for my research question.

Your file should now match the completed version for this step.

✔️ Awesome, I've got everything!

Your parser, evidence request, and main flow are all in place.

ⓧ I'd like to double check the full code

from exa_py import Exa


def parse_domains(raw_domains):
    domains = [
        domain.strip()
        for domain in raw_domains.split(",")
        if domain.strip()
    ]
    return domains or None


def search_web(query, include_domains):
    exa = Exa()
    return exa.search(
        query,
        type="auto",
        num_results=5,
        include_domains=include_domains,
        contents={"highlights": {"max_characters": 1000}},
    )


def main():
    query = input("Research question: ").strip()
    if not query:
        raise SystemExit("A research question is required.")

    raw_domains = input(
        "Optional domains, comma-separated (press Enter for all): "
    ).strip()
    include_domains = parse_domains(raw_domains)

    response = search_web(query, include_domains)

    for index, result in enumerate(response.results, start=1):
        print(f"{index}. {result.title or 'Untitled source'}")
        print(f"   {result.url}")
        print(f"   Published: {result.published_date or 'Date unavailable'}")
        for highlight in result.highlights or ["No highlight returned."]:
            print(f"   Evidence: {highlight}")


if __name__ == "__main__":
    main()

Your console now contains source metadata and query-relevant evidence. Next, you'll turn those temporary results into a reusable Markdown research brief.

Save a Shareable Research Brief

Your scout now retrieves useful evidence from the live web. However, terminal output disappears into scrollback and is awkward to share.

In this step, you'll turn each response into a Markdown brief. The script saves five source sections with metadata and evidence in research_report.md.

In this step, get ready to:
  • Build a Markdown report from the retrieved sources.
  • Save the report with UTF-8 encoding.
  • Open the report and confirm that its source links are usable.
Set the report path

A Path object gives the script one reusable location for the report. The path points to research_report.md inside the open exa-research-scout folder.

  • In research_scout.py, find the existing import at the top of the file.
  • Add the following import above it.
  • Add the report path below the imports.
from pathlib import Path

from exa_py import Exa


REPORT_PATH = Path("research_report.md")

What Does This Code Do?

  • The Path import provides file-path operations for the report.
  • The REPORT_PATH constant keeps the output filename in one place.
  • Save research_scout.py.
  • Confirm that REPORT_PATH appears below the imports.

Seeing an Import Error?

Check that the first line reads from pathlib import Path. Make sure REPORT_PATH uses matching capital letters everywhere.

Ask for help with the exact top section of your file: Help me fix the Path import and REPORT_PATH definition in research_scout.py.

Build the Markdown report

The response already contains the fields needed for a compact brief. A dedicated function can transform those result objects into Markdown while safely handling missing metadata.

  • In research_scout.py, find the end of search_web().
  • Add build_report() above main() by copying this code:
def build_report(query, response):
    lines = [
        f"# Research Brief: {query}",
        "",
        f"Sources found: {len(response.results)}",
        "",
    ]

    for index, result in enumerate(response.results, start=1):
        title = result.title or "Untitled source"
        published_date = result.published_date or "Date unavailable"
        highlights = result.highlights or ["No highlight returned."]

        lines.extend(
            [
                f"## {index}. {title}",
                "",
                f"- URL: {result.url}",
                f"- Published: {published_date}",
                "",
                "### Evidence",
                "",
            ]
        )
        lines.extend(f"- {highlight}" for highlight in highlights)
        lines.append("")

    return "\n".join(lines)

How Does the Report Builder Work?

  • The opening list creates the report heading and records how many sources Exa returned.
  • The loop creates one numbered section for every result.
  • The fallback values keep the report readable when a title, publication date, or highlight is missing.
  • The final join combines the lines into one Markdown string.
  • Save research_scout.py.
  • Check the new function for the three fallback values Untitled source, Date unavailable, and No highlight returned..

Seeing a Syntax Error?

Check that every opening bracket has a matching closing bracket. Keep return "\n".join(lines) aligned with the first line inside build_report().

Share the function with the helper: Help me find the syntax or indentation error in my build_report function.

Write and verify the brief

The final change replaces temporary console output with a saved file. UTF-8 encoding keeps the report reliable when sources contain international characters.

  • In main(), find this existing block:
    response = search_web(query, include_domains)

    for index, result in enumerate(response.results, start=1):
        print(f"{index}. {result.title or 'Untitled source'}")
        print(f"   {result.url}")
        print(f"   Published: {result.published_date or 'Date unavailable'}")
        for highlight in result.highlights or ["No highlight returned."]:
            print(f"   Evidence: {highlight}")
  • Replace that block with this code:
    response = search_web(query, include_domains)
    report = build_report(query, response)
    REPORT_PATH.write_text(report, encoding="utf-8")

    print(
        f"Saved {len(response.results)} sources to {REPORT_PATH.resolve()}"
    )

What Does This Change Do?

  • The build_report() call converts the search response into Markdown.
  • The write_text() call saves that string to the report path with UTF-8 encoding.
  • The final message reports the source count and the full location of the saved file.

Before you run the finished scout, what structure do you expect the saved report to contain?

  • Save research_scout.py.
  • Run the finished scout from the active integrated terminal by entering this command:
python research_scout.py
  • Enter the research question you chose earlier.
  • Press Enter at the optional domains prompt to search all domains.

You'll see a message confirming that five sources were saved to the resolved research_report.md path. That's the research scout complete: its evidence now survives the terminal session.

  • Select research_report.md in the Visual Studio Code Explorer sidebar.
  • Confirm that the report contains a research brief heading.
  • Confirm that the report contains five numbered source sections.
  • Confirm that each source shows a URL.
  • Confirm that each source shows a publication date or its fallback.
  • Confirm that each source includes evidence bullets.
  • Follow one URL to confirm that the source opens in your browser.

Report Missing or Incomplete?

If the report is missing, confirm that you ran the script from the open exa-research-scout folder. If sections are missing, compare the indentation inside the build_report() loop.

Use the saved message to locate the file. For targeted help, ask: Why is research_report.md missing or incomplete after I run research_scout.py?

✔️ Awesome, I've got everything!

Great work. Your saved brief now packages five sources with dates and evidence into a file you can reopen or share.

ⓧ I'd like to double check the full code

from pathlib import Path

from exa_py import Exa


REPORT_PATH = Path("research_report.md")


def parse_domains(raw_domains):
    domains = [
        domain.strip()
        for domain in raw_domains.split(",")
        if domain.strip()
    ]
    return domains or None


def search_web(query, include_domains):
    exa = Exa()
    return exa.search(
        query,
        type="auto",
        num_results=5,
        include_domains=include_domains,
        contents={"highlights": {"max_characters": 1000}},
    )


def build_report(query, response):
    lines = [
        f"# Research Brief: {query}",
        "",
        f"Sources found: {len(response.results)}",
        "",
    ]

    for index, result in enumerate(response.results, start=1):
        title = result.title or "Untitled source"
        published_date = result.published_date or "Date unavailable"
        highlights = result.highlights or ["No highlight returned."]

        lines.extend(
            [
                f"## {index}. {title}",
                "",
                f"- URL: {result.url}",
                f"- Published: {published_date}",
                "",
                "### Evidence",
                "",
            ]
        )
        lines.extend(f"- {highlight}" for highlight in highlights)
        lines.append("")

    return "\n".join(lines)


def main():
    query = input("Research question: ").strip()
    if not query:
        raise SystemExit("A research question is required.")

    raw_domains = input(
        "Optional domains, comma-separated (press Enter for all): "
    ).strip()
    include_domains = parse_domains(raw_domains)

    response = search_web(query, include_domains)
    report = build_report(query, response)
    REPORT_PATH.write_text(report, encoding="utf-8")

    print(
        f"Saved {len(response.results)} sources to {REPORT_PATH.resolve()}"
    )


if __name__ == "__main__":
    main()

You now have a reusable research scout that turns live search results into a source-linked brief. You've completed the main project, with an optional Secret Mission ready to push the scout further.

Secret mission

Add a Publication-Date Filter

Time-sensitive research needs a clear boundary between recent evidence and older material. Extend your scout with an optional publication date so the generated report shows when the search begins.

Clean Up Your Resources

Clean Up Your Resources

Your scout has no server or scheduled job running.

Keeping the exa-research-scout folder costs nothing while the script is idle.

Cost warning

Each future standard Exa search request costs $0.007 before available credits are applied. Retries also count as future requests.

  • Leave automatic credit recharge disabled unless you intend to pay for requests beyond your available credits.

Resources you used:

  • Local exa-research-scout folder containing .venv, requirements.txt, research_scout.py, and research_report.md.
  • Current Visual Studio Code integrated terminal session holding the exported EXA_API_KEY value.

Keep everything running

Keeping the folder requires no cleanup. Choose this option if you plan to run new research questions.

  • No search runs in the background.
  • The .venv environment stays available for future runs.
  • The latest research_report.md stays ready to review or share.
  • The current EXA_API_KEY export lasts only until this terminal session closes.

Pause - I'll come back to this later

Pausing ends the session that holds your API key. Every project file stays in place.

  • Close Visual Studio Code to end the active integrated terminal session.
  • Leave the exa-research-scout folder on your Mac.
  • Export EXA_API_KEY again in a new integrated terminal before your next run.

The closed session removes the session-only key from memory. Your script and report remain unchanged.

Delete - I don't want to use this again

Deleting the folder is permanent. Your report remains safe if you copy it elsewhere before the cleanup.

  • Copy research_report.md outside the exa-research-scout folder if you want to keep the latest brief.
  • Return to the active integrated terminal from earlier.
  • Delete the project folder from its parent directory by running these commands:
cd ..
rm -rf exa-research-scout

What do these commands do?

  • The first line moves the terminal to the folder that contains your project.
  • The second line removes the project folder plus every file inside it.
  • Confirm that Visual Studio Code no longer shows the files from exa-research-scout.
  • Close the integrated terminal session to clear the session-only API key.

Still see the project files?

Check that the first command moved you to the folder directly above exa-research-scout.

Check the spelling of the folder name before trying the delete command again.

Help me safely remove my local project folder.

That closes the loop. Your local environment and project files are now removed.

Nice Work!

Nice Work!

You did it! Your Python research scout now turns live results from the Exa Search API into a source-linked Markdown brief.

What you learned:

  • Built a Python command-line research scout that searches the live web with natural-language questions.
  • Added evidence highlights that surface query-relevant passages. Added optional domain filtering that narrows the source pool.
  • Generated a reusable Markdown research brief in research_report.md. It packages each source's URL, publication date, and evidence highlights.
  • Secret Mission: Extended the scout with an optional publication-date filter. The report records the chosen boundary and contains dated results returned after it.

Ready to quiz yourself?