ScreenshotNeo

BlogHow-to

How to Install Firefox Extensions With Selenium in Python

Install a signed Firefox add-on or temporary development extension in Selenium Python, then handle profiles, remote sessions, common errors, and cleanup.

By the ScreenshotNeo team4 October 20266 min read

To install a Firefox extension with Selenium Python, start the Firefox WebDriver and call driver.install_addon() with the absolute path to the extension package. Use the default temporary=False for a signed add-on such as a published .xpi. For an unsigned development extension, pass temporary=True and provide its unpacked directory or a zip package. The method returns an add-on ID that you can pass to driver.uninstall_addon().

1. Install Selenium and prepare Firefox

Install or upgrade Selenium in the Python environment that will run your script:

python -m pip install -U selenium

Current Selenium Python documentation lists Python 3.10 or newer. Selenium Manager can handle browser and driver setup for most supported platforms when a WebDriver starts. You can also install and specify Firefox and GeckoDriver explicitly if your environment requires it. Selenium 4’s Firefox guide lists Firefox 78 or newer and recommends the latest GeckoDriver. These are compatibility prerequisites; they do not guarantee that any particular add-on works with every Firefox version.

2. Install a signed extension

For a published add-on, obtain its signed .xpi package, then install it after creating the driver. Resolve the path before passing it to Selenium; the API expects an absolute path.

from pathlib import Path
from selenium import webdriver

extension_path = Path("extensions/my_extension.xpi").resolve()
driver = webdriver.Firefox()

try:
    addon_id = driver.install_addon(str(extension_path))
    print(f"Installed add-on: {addon_id}")

    driver.get("https://example.com")
    # Run browser automation with the extension installed.
finally:
    driver.quit()

Replace extensions/my_extension.xpi with the location of your file. The installation call belongs after webdriver.Firefox(), because it installs the add-on into the running browser session.

3. Install an unsigned extension during development

Unfinished or unpublished extensions are often unsigned. Selenium’s Firefox guide says these can only be installed temporarily. Pass temporary=True with the absolute path to the unpacked extension directory or a zip package:

from pathlib import Path
from selenium import webdriver

extension_path = Path("build/my_extension").resolve()
driver = webdriver.Firefox()

try:
    addon_id = driver.install_addon(str(extension_path), temporary=True)
    print(f"Temporarily installed add-on: {addon_id}")

    driver.get("https://example.com")
    # Exercise the development extension in this session.
finally:
    driver.quit()

A temporary install is scoped to the session. Do not treat it as a persistent installation for later Firefox runs.

4. Uninstall an add-on before quitting

install_addon() returns an identifier. Keep it if you need to remove the extension during the same session:

from pathlib import Path
from selenium import webdriver

extension_path = Path("extensions/my_extension.xpi").resolve()
driver = webdriver.Firefox()
addon_id = None

try:
    addon_id = driver.install_addon(str(extension_path))
    driver.get("https://example.com")
    # Run automation that needs the extension.
finally:
    if addon_id is not None:
        driver.uninstall_addon(addon_id)
    driver.quit()

Use try/finally so the browser is shut down if navigation or automation raises an exception. If installation itself fails, addon_id remains None and the cleanup still closes the browser.

5. Choose the right artifact and session setup

Situation Artifact and call What to expect
Published add-on Signed .xpi; install_addon(path) Normal route for a signed add-on.
Unfinished or unpublished add-on Unpacked directory or zip; install_addon(path, temporary=True) Temporary installation for the session.
Local WebDriver Path visible to the Python process and local browser setup Resolve and pass an absolute path.
Remote WebDriver or Grid Artifact must be made available to the remote browser setup File-transfer details depend on the Grid deployment; a client-side path is not automatically available on a remote node.

Using a Firefox profile

The Firefox profile API can clone a profile directory when you pass it to the documented Python FirefoxProfile constructor, and it exposes profile preference and path methods. That profile support does not change the current add-on installation sequence: use driver.install_addon() after the WebDriver starts. Avoid relying on older profile add_extension() examples as the current Selenium installation method.

Remote WebDriver and Grid

For a remote session, account for where the extension file is read and how it reaches the browser node. A path on the machine running your Python script may not exist on the Grid node. Check the documentation for your specific remote setup for its artifact handling procedure; there is no single transfer command established for every Grid deployment. Once the artifact is available through that setup, use the supported remote driver flow and verify the add-on is active in the remote browser.

6. Or skip the browser setup

If your goal is to capture a website screenshot rather than exercise a Firefox extension, ScreenshotNeo returns a screenshot or PDF from one GET request. The API accepts options for full-page capture, element selection, viewport and device settings, waiting, headers, cookies, and more. See the ScreenshotNeo API documentation for the available parameters.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server offers screenshot, page-info, and PDF-capture tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.

7. Troubleshooting

Symptom Likely cause Fix
File or path not found The relative path resolves from a different working directory, or the artifact is absent. Check that the file or directory exists, then use Path(...).resolve() and pass the resulting absolute path.
Unsigned extension is rejected The extension is unfinished or unpublished but was installed as a normal add-on. Use its unpacked directory or zip and set temporary=True.
Installation fails on a remote node The extension path is local to the client and unavailable to the browser node. Use the artifact-transfer mechanism documented for your Grid or remote provider, then install from the path supported there.
Firefox or driver fails to start Firefox/GeckoDriver availability or compatibility is wrong for the environment. Confirm the Firefox installation, use a supported Firefox version (Selenium 4’s guide specifies 78 or newer), and update GeckoDriver. Review Selenium Manager output or configure the browser and driver explicitly.
Add-on installs but the expected behavior is absent The add-on may not support that Firefox version, may need configuration, or may not be active on the page or context being automated. Check the add-on’s own compatibility and setup guidance, and confirm the browser session and target page match its requirements.
Uninstall raises an error The install did not return an ID, or the ID was not retained correctly. Only call uninstall_addon() with the identifier returned by install_addon(); guard cleanup when installation may fail.

8. Performance, reliability, and cost

  • Startup: Firefox and driver startup plus extension installation add work before page automation. Install once per browser session and reuse that session for the tasks that need the add-on, rather than repeatedly starting Firefox for each page.
  • Reliability: Pin or otherwise control your Selenium, Firefox, GeckoDriver, and extension artifact versions when repeatable automation matters. The official minimum version does not promise compatibility for a particular add-on.
  • Artifact handling: Use a stable absolute path locally. For remote sessions, make artifact availability explicit and follow the deployment’s transfer process.
  • Cost: Selenium is an automation library; the cited setup does not establish a Selenium usage fee. Compute and browser infrastructure costs depend on where and how often you run Firefox. The add-on’s own license or service terms are separate.

9. FAQ

Does the extension survive after Firefox closes?

A temporary development install is only for the current session. Use a signed add-on for the normal published-extension route.

Can I install an extension before creating the driver?

The current Selenium Firefox workflow installs the add-on after the WebDriver has started, using driver.install_addon().

Can I use the returned add-on ID later?

Use it with driver.uninstall_addon(addon_id) during the active session to remove that installed add-on.

Will my local extension path work with Selenium Grid?

Not necessarily. The remote browser must be able to access the artifact through the remote setup’s supported file-handling process.

Official references