🌐 URL parsing and manipulation made easy.



furl is a small Python library that makes parsing and
manipulating URLs easy.

Python's standard urllib and urlparse modules provide a number of URL
related functions, but using these functions to perform common URL
operations proves tedious. Furl makes parsing and manipulating URLs

Furl is well tested, Unlicensed in the public domain, and supports
Python 2, Python 3, PyPy2, and PyPy3.

Code time: Paths and query arguments are easy. Really easy.

>>> from furl import furl
>>> f = furl('http://www.google.com/?one=1&two=2')
>>> f /= 'path'
>>> del f.args['one']
>>> f.args['three'] = '3'
>>> f.url

Or use furl's inline modification methods.

>>> furl('http://www.google.com/?one=1').add({'two':'2'}).url

>>> furl('http://www.google.com/?one=1&two=2').set({'three':'3'}).url

>>> furl('http://www.google.com/?one=1&two=2').remove(['one']).url

Encoding is handled for you. Unicode, too.

>>> f = furl('http://www.google.com/')
>>> f.path = 'some encoding here'
>>> f.args['and some encoding'] = 'here, too'
>>> f.url
>>> f.set(host=u'ドパむン.γƒ†γ‚Ήγƒˆ', path=u'Π΄ΠΆΠΊ', query=u'β˜ƒ=☺')
>>> f.url

Fragments also have a path and a query.

>>> f = furl('http://www.google.com/')
>>> f.fragment.path.segments = ['two', 'directories']
>>> f.fragment.args = {'one': 'argument'}
>>> f.url

Or get fancy.

>>> f = furl('http://www.google.com/search?q=query#1')
>>> f.copy().remove(path=True).set(host='taco.com')
...  .join('/pumps.html').add(fragment_path='party').url
>>> f.asdict()
{ 'url': 'http://taco.com/pumps.html#party',
  'scheme': 'http',
  'username': None,
  'password': None,
  'host': 'taco.com',
  'host_encoded': 'taco.com',
  'port': 80,
  'netloc': 'taco.com',
  'origin': 'http://taco.com',
  'path': { 'encoded': '/pumps.html',
            'isabsolute': True,
            'isdir': False,
            'isfile': True,
            'segments': ['pumps.html']},
  'query': { 'encoded': '',
             'params': []},
  'fragment': { 'encoded': 'party',
                'path': { 'encoded': 'party',
                          'isabsolute': False,
                          'isdir': False,
                          'isfile': True,
                          'segments': ['party']},
                'query': { 'encoded': '',
                           'params': []},
                'separator': True}, }


See more furl magic and examples in furl's API document, API.md.


Installing furl with pip is easy.

$ pip install furl
  • Should percent encode the query key and value when being encoded as str

    Should percent encode the query key and value when being encoded as str

    I use furl(v1.1) in http proxy app, and receive url in server side(the http proxy received from client):

    >>> url_received = 'http://example.com/?redirect=http%3A%2F%2Fwww.example.com%2F'
    >>> parsed = furl.furl(url_received)

    After do something with the parsed url, and generate url in client side(to be sent to the real server):

    >>> url_to_be_sent = parsed.url 
    >>> print(url_to_be_sent)
    >>> print(url_to_be_sent.query)

    Should the "redirect=http://www.example.com/" part be percent encoded as original query "redirect=http%3A%2F%2Fwww.example.com%2F"?

    opened by xiren7 28
  • test_join test fails

    test_join test fails

    Hey folks. Thanks for your work on this project!

    I wanted to package furl for NixOS, and the build throws this error for me:

    FAIL: test_join (test_furl.TestFurl)
    Traceback (most recent call last):
      File "/build/furl-2.1.0/tests/test_furl.py", line 2042, in test_join
        assert f.url == 'wss://slrp.com/foo:1'
    Ran 76 tests in 9.423s

    It seems only this single test fails, all others are fine? That leads me to believe it's not a peculiarity of my packaging that's responsible.

    Anyways, for context here's the build nix expression I used:

    { lib, python3, radicale2 }:
    with python3.pkgs;
    buildPythonPackage rec {
        pname = "furl";
        version = "2.1.0";
        src = fetchPypi {
          inherit pname version;
          sha256 = "08dnw3bs1mk0f1ccn466a5a7fi1ivwrp0jspav9arqpf3wd27q60";
        propagatedBuildInputs = [
          orderedmultidict six
        checkInputs = [
    opened by Valodim 17
  • Missing __hash__ method with Python 3.x

    Missing __hash__ method with Python 3.x


    I'm using your module directly and indirectly via the SQLAlchemy-Utils module and its URLType column type which coerces a URL into a furl object.

    I've found an issue when I try and use the URLType type on Python 3.x, I get the following error when I try and query an object using such a column:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2588, in all
        return list(self)
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/loading.py", line 86, in instances
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 189, in raise_from_cause
        reraise(type(exception), exception, tb=exc_tb, cause=exc_value)
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 183, in reraise
        raise value
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/loading.py", line 71, in instances
        rows = [proc(row) for row in fetch]
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/loading.py", line 71, in <listcomp>
        rows = [proc(row) for row in fetch]
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/loading.py", line 379, in _instance
        instance = session_identity_map.get(identitykey)
      File ".../venv-3.5/lib/python3.5/site-packages/sqlalchemy/orm/identity.py", line 146, in get
        if key not in self._dict:
    TypeError: unhashable type: 'furl'

    I've managed to fix this by monkeypatching the furl class with a __hash__ method like so:

    from sqlalchemy_utils import URLType
    from furl import furl
    def furl_hash(self):
        return hash(self.url)
    furl.__hash__ = furl_hash
    class Foo(Model):
        url = Column(URLType)

    Would it be possible to add such a method? I'm not sure if I'm 100% correct just using the value of self.url here but it looked close enough.

    opened by bodgit 10
  • we can't choose encoding of final url

    we can't choose encoding of final url

    this module is great and easy when manipulating urls

    adding "encoding" parameter to ".tostr" function would be nice rather than encoding everything in "utf8" since "utf8" is not only option in real life


    opened by tuntapovski 10
  • added JSON output for URL

    added JSON output for URL

    opened by NoobSkywalker 8
  • Add test for unicode keys and values

    Add test for unicode keys and values

    This implements the test from the comments of https://github.com/gruns/furl/issues/32 and demonstrates a regression (bug was reported in 0.3.mumble, fixed in 0.3.8, and is now broken in 0.4.1 and 0.4.2)

    opened by doismellburning 8
  • New bugs with Python 3.9.2+

    New bugs with Python 3.9.2+

    Python 3.9.1 #121

    FAIL: test_join (test_furl.TestFurl)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 2060, in test_join
        assert f.url == 'wss://slrp.com/foo:1'

    Python 3.9.2

    FAIL: test_join (test_furl.TestFurl)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 2060, in test_join
        assert f.url == 'wss://slrp.com/foo:1'
    FAIL: test_add (test_furl.TestQuery)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 678, in test_add
        assert q.params.allitems() == runningsum
    FAIL: test_params (test_furl.TestQuery)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 806, in test_params
        assert item1 == item2
    FAIL: test_remove (test_furl.TestQuery)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 720, in test_remove
        assert len(q.params) == 0
    FAIL: test_set (test_furl.TestQuery)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 687, in test_set
        assert q.params.allitems() == items_omd.allitems()
    FAIL: test_various (test_furl.TestQuery)
    Traceback (most recent call last):
      File "/home/shadchin/github/furl/tests/test_furl.py", line 650, in test_various
        assert q.params.allitems() == items.allitems()
    opened by shadchin 7
  • [Improvement] Fix code quality issues

    [Improvement] Fix code quality issues


    Hi :wave: I ran the DeepSource static analyzer on the forked copy of this repo and found some interesting code quality issues. This PR fixes a few of them.

    Summary of changes

    • fixed ambiguous variable names
    • Used literal syntax instead of function calls to create data structure
    • Refactored if expression
    • Refactored unnecessary else / elif when if block has a return statement
    • added .deepsource.toml config
    opened by withshubh 7
  • Why ``furl.set`` call ``self.query.load`` not ``self.query.set``?

    Why ``furl.set`` call ``self.query.load`` not ``self.query.set``?

    In [1]: furl('http://www.google.com/?one=1&two=2').set({'one': 'one'})
    Out[1]: furl('http://www.google.com/?one=one')
    In [2]: furl('http://www.google.com/?one=1&two=2').remove('one').add({'one': 'one'})
    Out[2]: furl('http://www.google.com/?two=2&one=one')

    How to realize In [2] more simply

    opened by Brightcells 7
  • 'Module_six_moves_urllib_parse' object has no attribute 'SplitResult'

    'Module_six_moves_urllib_parse' object has no attribute 'SplitResult'

    I do not get this error with furl-0.3.7. I get this error with furl-0.4.2.

    In [3]: furl.furl()
    AttributeError                            Traceback (most recent call last)
    <ipython-input-3-a2bd23daefba> in <module>()
    ----> 1 furl.furl()
    /Library/Python/2.7/site-packages/furl/furl.pyc in __init__(self, url, strict)
        847         self.strict = strict
    --> 849         self.load(url)  # Raises ValueError on invalid url.
        851     def load(self, url):
    /Library/Python/2.7/site-packages/furl/furl.pyc in load(self, url)
        865         # Python 2.7+. In Python <= 2.6, urlsplit() doesn't raise a
        866         # ValueError on malformed IPv6 addresses.
    --> 867         tokens = urlsplit(url)
        869         self.netloc = tokens.netloc  # Raises ValueError in Python 2.7+.
    /Library/Python/2.7/site-packages/furl/furl.pyc in urlsplit(url)
       1308         url = _set_scheme(url, 'http')
       1309     toks = urllib.parse.urlsplit(url)
    -> 1310     return urllib.parse.SplitResult(*_change_urltoks_scheme(toks, original_scheme))
    AttributeError: 'Module_six_moves_urllib_parse' object has no attribute 'SplitResult'
    opened by dydt 7
  • Add typing support

    Add typing support

    Currently running mypy against something using this project gets error: Skipping analyzing 'furl': found module but no type hints or library stubs

    opened by palfrey 3
  • furl doesn't type-hint itself as Text

    furl doesn't type-hint itself as Text

    If furl were a typing.Text, it would work with static type checkers/hinters.

    I like that the following works:

    url_obj = furl('http://www.google.com')
    response = requests.get(url_obj)

    However, the static checker (I'm using PyRight) complains that furl.furl.furl does not match request.post's signature for Text | bytes.

    I'm newish to Python still, but I think it would require adding the superclass:

    from typing import Text
    # at https://github.com/gruns/furl/blob/d0bee9a27d7f432b047194a94f64cd4ff0319f6a/furl/furl.py#L1337-L1338
    class furl(URLPathCompositionInterface, QueryCompositionInterface,
               FragmentCompositionInterface, UnicodeMixin, Text):

    Then using a factory function named furl.

    In the meantime, this is my workaround to satisfy the checker:

    # in my utils/__init__.py
    from typing import Text
    from furl import furl as FurlOrig
    class Furl(FurlOrig, Text):  # bonus: capitalized class name less surprising
    def furl(*args, **kwargs) -> Furl:
        return Furl(*args, **kwargs)
    # usage in other files
    from utils import furl
    # use `furl` as normal
    opened by Kache 3
  • if path end with

    if path end with "/", then add one path start with "/" will become "//"

    In [2]: u = furl("https://www.baidu.com/aaa") / "/bbbb/ccc" In [3]: u.url Out[3]: 'https://www.baidu.com/aaa/bbbb/ccc' In [4]: u = furl("https://www.baidu.com/aaa/") / "/bbbb/ccc" In [6]: u.url Out[6]: 'https://www.baidu.com/aaa//bbbb/ccc'

    opened by sankforever 1
  • Loosing absolute path on assignation

    Loosing absolute path on assignation


    When playing with file://uris I found a weird behaviour related to path setter.

    Steps to reproduce

    >>> x = furl('file:///a/b')
    >>> x.path
    >>> x.path.isabsolute # The path here is absolute and is ok
    >>> x.path = x.path / 'c'
    >>> x.path  # Once set to the path attribute of a furl object the path is not absolute anymore
    >>> x.path.isabsolute 

    Additional info & workaround

    This problem does not happen whan assigning a path as a string

    >>> x = furl('file:///a/b')
    >>> x.path = str(x.path / 'c')
    >>> x.path 
    >>> x.path.isabsolute 

    Expected behaviour

    The concatenation of a path segment should not change the way the path interpreted

    opened by rsemlal 1
  • default port not properly removed with set(netloc=None) and weird interaction with set(scheme=None)

    default port not properly removed with set(netloc=None) and weird interaction with set(scheme=None)


    >>> f = furl('https://www.google.com/hello')
    >>> f.copy().set(scheme=None, netloc=None).url


    >>> f = furl('https://www.google.com/hello')
    >>> f.copy().set(scheme=None).set(netloc=None).url

    and oddity with .set(scheme=None)

    >>> f = furl('https://www.google.com:999/hello')
    >>> f.copy().set(scheme=None, netloc=None).url
    opened by gruns 0
  • Triple slashes

    Triple slashes

    Although it is okay, still looks weird:

    >>> f = furl('staging.s3-website.us-east-1.amazonaws.com')
    >>> f.scheme = 'http'
    >>> f.url

    Why is there triple slashes?


    >>> f = furl('http://staging.s3-website.us-east-1.amazonaws.com')
    >>> f.url


    >>> f = furl('staging.s3-website.us-east-1.amazonaws.com')
    >>> f.scheme = 'http://'  # yes, it's not a proper way, but still confusing how parser works

    I'm confused.

    opened by kaldown 1
  • [Request] Domain manipulation

    [Request] Domain manipulation

    Is there any useful way to manipulate domains?

    Reasoning: let's say I have https://s3.amazonaws.com I need to build endpoint with bucket name f.domain.add('my-bucket')

    then I need to create website out of it:

    >> f.url
    >>> f.domain.part[1] = "s3-website-region"

    In total:

    >>> f.url
    opened by kaldown 1
  • add `replace()` method

    add `replace()` method

    It would be great if there would be a replace method, so that:

    >> furl(`http:\\example.com\?a=1&b=2`).replace({'a':100}).url

    would output: http:\\example.com\?a=100&b=2

    A remove and add would change the order of things:

    >> furl(`http:\\example.com\?a=1&b=2`).remove('a').add({'a':100}).url

    would output http:\\example.com\?b=2&a=100, instead

    Although http:\\example.com\?a=100&b=2 is equivalent to http:\\example.com\?b=2&a=100 according to the the standards, in practice, it is useful to keep order when you end with millions of URLs and you want to keep things organized for data processing. I currently have a pandas dataframe (df) in which I use apply to modify a subset of rows with apply:

    df.loc[criteria, ['url', 'value']].apply(lambda x: furl(x).remove('a').add('{a: 100}'), axis=1)

    However, this would change the "order" of the URL in the subset of criteria, as compared to all the rest.

    so, I created the following function to circumvent the problem:

    from furl import furl
    def replace_param(url, param, value):
        _f = furl(url)
        _f.args[param] = value
        return _f.url
    # small test
    replace_param("http:\\example.com\?a=1&b=2", 'a', 100)
    # pandas usage
    df.loc[criteria, ['url', 'value']].apply(lambda x: furl(x).replace_param('a', 100), axis=1)

    I know the internals of furl could easily support the replace function, so this should be fairly easy to implement.

    opened by robertour 0
  • Add support array index

    Add support array index

    Propose to add support array as arg. For example

    print(furl('http://example.org', args={'f': [1, 2, 3]}))

    should return

    opened by heckad 5
  • v2.1.2(Apr 12, 2021)

    Fixed: Support Python 3.9's changed urllib.parse.urljoin() behavior.

    • < py3.9: furl('wss://slrp.com/').join('foo:1') -> 'wss://slrp.com/foo:1'
    • >= py3.9: furl('wss://slrp.com/').join('foo:1') -> 'foo:1'

    Changed: Drop semicolon query delimiters. See https://bugs.python.org/issue42967. Changed: Drop support for EOL Python 3.4 and Python 3.5.

    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Apr 9, 2021)

    Fixed: Export metadata variables (furl.__title__, furl.__version__, etc). Added: scheme, host, netloc, and origin as parameters to furl.remove(). Changed: Homogenize parameter order across furl.add(), furl.set(), and furl.remove(). Changed: furl.origin can be assigned None. This has the same behavior as furl.remove(origin=True).

    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Sep 20, 2019)

    • Added: a dont_quote= parameter to Query.encode() and a query_dont_quote= parameter to furl.tostr() that exempt valid query characters from being percent-encoded, either in their entirety with dont_quote=True, or selectively with dont_quote=<string>, like dont_quote='/[email protected]_'.

    • Changed: Move package info from __init__.py into the more standard __version__.py.

    • Fixed: Support Unicode usernames and passwords in Python 2.

    • Fixed: Update orderedmultdict to v1.0.1 to resolve a DeprecationWarning.

    • Fixed: Encode '/' consistently in query strings across both quote_plus=True and quote_plus=False.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Oct 16, 2018)

    • Added: All URL components (scheme, host, path, etc) to furl()'s constructor as keyword arguments. E.g. f = furl(scheme='http', host='host', path='/lolsup').

    • Changed: furl.truediv() and Path.truediv() now mirror Pathlib.truediv()'s behavior and return a new instance. The original instance is no longer modified. Old behavior: f = furl('1'); f / '2' -> str(f) == '1'. New behavior: f = furl('1'); f /= '2' -> str(f) == '1/2'.

    • Fixed: Path.load() now accepts Path instances, e.g. f.path.load(Path('hi')).

    • Removed: Support for Python 2.6, which reached EOL on 2013-10-29.

    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Aug 22, 2018)

  • v1.2(Jun 29, 2018)

    • Added: Path segment appending via the division operator (__truediv__()).
    • Changed: Bump orderedmultidict dependency to v1.0.
    • Changed: Check code style with flake8 instead of pycodestyle.
    • Changed: Percent-encode all non-unreserved characters in Query key=value pairs, including valid query characters (e.g. =, ?, etc). Old encoding: ?url=http://foo.com/; new encoding: ?url=http%3A%2F%2Ffoo.com%2F. Equal signs remain decoded in query values where the key is empty to allow for, and preserve, queries like ?==3==.
    Source code(tar.gz)
    Source code(zip)
  • v1.1(May 31, 2018)

    • Fixed: Support and preserve all query strings as provided. For example, preserve the query &&== of http://foo.com?&&== as-is. Empty key=value pairs are stored as ('', None) in Query.params, e.g. [('', None), ('', None)] for the query &.
    • Changed: Don't encode equal signs (=) in query values if the key is empty. That is, allow and preserve queries like ?==3== while also percent encoding equal signs in query values with an associted key, as expected. E.g. ?a=1%3D1.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.2(May 28, 2018)

    • Added: strip_scheme() public function.
    • Changed: Make get_scheme() and set_scheme() functions public.
    • Added: Support all schemes without a netloc/authority, like mailto:[email protected], without an explicit whitelist of such schemes (e.g. tel:, sms:, mailto:, etc).
    • Fixed: Restore furl.url's setter method. E.g. furl.url = 'http://www.foo.com/'.
    • Removed: Support for Python 3.3, which reached EOL on 2017-09-29.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Nov 20, 2017)

  • v1.0.0(Nov 20, 2017)

    • Added: Test against Python 3.6.
    • Changed: Bumped the version number to v1.0 to signify that furl is a mature and stable library. Furl has been marked Production/Stable in setup.py for a long time anyhow -- it's high time for the version number to catch up.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.7(Mar 1, 2017)

  • 0.5.6(Oct 23, 2016)

  • 0.5.3(Sep 23, 2016)

  • v0.5.2(Aug 14, 2016)

  • v0.5.1(Jun 29, 2016)

Ansgar Grunseid
Ansgar Grunseid
A simple, immutable URL class with a clean API for interrogation and manipulation.

purl - A simple Python URL class A simple, immutable URL class with a clean API for interrogation and manipulation. Supports Pythons 2.7, 3.3, 3.4, 3.

David Winterbottom 257 Oct 9, 2021
find all the URL of a site with a specific Regex

href this program will find all the link with a spesfic Regex pattern from a site. what it will do in any site there are a lots of url that may you ne

Arya Shabane 9 Oct 17, 2021
A URL builder for genius :D

genius-url A URL builder for genius :D Usage from gurl import genius_url

κŒ—α–˜κ’’κ€€κ“„κ’’κ€€κˆ€κŸ 12 Aug 14, 2021
A python code for url redirect check

A python code for url redirect check

Fayas Noushad 1 Oct 24, 2021
This is a no-bullshit file hosting and URL shortening service that also runs 0x0.st. Use with uWSGI.

This is a no-bullshit file hosting and URL shortening service that also runs 0x0.st. Use with uWSGI.

mia 1.5k Oct 25, 2021
πŸ”— Generate Phishing URLs πŸ”—

URLer ?? Generate Phishing URLs ?? URLer Table Of Contents General Information Preview Installation Disclaimer Credits Social Media Bug Report General

mrblackx 4 Sep 27, 2021
A friendly library for parsing HTTP request arguments, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, Pyramid, webapp2, Falcon, and aiohttp.

webargs Homepage: https://webargs.readthedocs.io/ webargs is a Python library for parsing and validating HTTP request objects, with built-in support f

marshmallow-code 1.2k Oct 19, 2021
A url redirect status check module for python

A url redirect status check module for python

Fayas Noushad 2 Oct 24, 2021
UDdup - URLs Deduplication Tool

UDdup - URLs Deduplication Tool The tool gets a list of URLs, and removes "duplicate" pages in the sense of URL patterns that are probably repetitive

Rotem Reiss 125 Sep 8, 2021
URL Shortener in Flask - Web service using Flask framework for Shortener URLs

URL Shortener in Flask Web service using Flask framework for Shortener URLs Install Create Virtual env $ python3 -m venv env Install requirements.txt

Rafnix Guzman 1 Sep 21, 2021
a url shortener project from semicolonworld

Url Shortener With Django Written by Semicolon World

null 3 Aug 24, 2021
Astra is a tool to find URLs and secrets.

Astra finds urls, endpoints, aws buckets, api keys, tokens, etc from a given url/s. It combines the paths and endpoints with the given domain and give

Stinger 129 Oct 22, 2021
Have you ever wondered: Where does this link go? The REDLI Tool follows the path of the URL.

Have you ever wondered: Where does this link go? The REDLI Tool follows the path of the URL. It allows you to see the complete path a redirected URL goes through. It will show you the full redirection path of URLs, shortened links, or tiny URLs.

JAYAKUMAR 28 Oct 20, 2021
declutters url lists for crawling/pentesting

uro Using a URL list for security testing can be painful as there are a lot of URLs that have uninteresting/duplicate content; uro aims to solve that.

Somdev Sangwan 253 Oct 19, 2021
encurtador de links feito com python

curt-link encurtador de links feito com python! instalação Linux: $ git clone https://github.com/bydeathlxncer/curt-link $ cd curt-link $ python3 url.

bydeathlxncer 4 Sep 16, 2021
:electric_plug: Generating short urls with python has never been easier

pyshorteners A simple URL shortening API wrapper Python library. Installing pip install pyshorteners Documentation https://pyshorteners.readthedocs.i

Ellison 316 Oct 25, 2021