diff --git a/.gitignore b/.gitignore
index b6e83da..c1fee79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,5 +94,3 @@ ENV/
 doc/_build
 .eggs
 bioutils/_data/assemblies/pull
-.pytest_cache
-.vscode
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index b933f71..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    "python.formatting.provider": "yapf",
-    "editor.formatOnSave": true,
-    "python.venvPath": "${workspaceFolder}/venv/",
-    "python.pythonPath": "${workspaceFolder}/venv/3.6/bin/python",
-}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 5ce8a7b..367f174 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ SELF:=$(firstword $(MAKEFILE_LIST))
 PKG=bioutils
 PKGD=$(subst .,/,${PKG})
 
-VEDIR=venv/3.6
+VEDIR=venv/3.5
 
 
 ############################################################################
diff --git a/README.rst b/README.rst
index a126048..224f8e9 100644
--- a/README.rst
+++ b/README.rst
@@ -9,6 +9,8 @@ the `hgvs <https://github.com/biocommons/hgvs/>`_ and `uta
 not really intended for broader use (read: it may change without
 notice).
 
+To use an E-Utilities API key run add it to an environment variable called `ncbi_api_key`
+and it will be used in the E-Utilities request.
 
 .. |issues_badge| image:: https://img.shields.io/github/issues/biocommons/bioutils.png
   :target: https://github.com/biocommons/bioutils/issues
diff --git a/bioutils/accessions.py b/bioutils/accessions.py
index c80a428..c10a9b5 100644
--- a/bioutils/accessions.py
+++ b/bioutils/accessions.py
@@ -5,154 +5,8 @@
 
 from __future__ import absolute_import, division, print_function, unicode_literals
 
-import re
-
 from six import iteritems
 
-from .exceptions import BioutilsError
-
-
-_ensembl_species_prefixes = "|".join("""ENS ENSACA ENSAME ENSAMX
-ENSANA ENSAPL ENSBTA ENSCAF ENSCAN ENSCAP ENSCAT ENSCCA ENSCEL ENSCGR
-ENSCGR ENSCHI ENSCHO ENSCIN ENSCJA ENSCLA ENSCPO ENSCSA ENSCSAV ENSDAR
-ENSDNO ENSDOR ENSEBU ENSECA ENSEEU ENSETE ENSFAL ENSFCA ENSFDA ENSGAC
-ENSGAL ENSGGO ENSGMO ENSHGLF ENSHGLM ENSJJA ENSLAC ENSLAF ENSLOC
-ENSMAU ENSMEU ENSMFA ENSMGA ENSMIC ENSMLE ENSMLU ENSMMU ENSMNE ENSMOC
-ENSMOD ENSMPU ENSMUS ENSNGA ENSNLE ENSOAN ENSOAR ENSOCU ENSODE ENSOGA
-ENSONI ENSOPR ENSORL ENSPAN ENSPCA ENSPCO ENSPEM ENSPFO ENSPMA ENSPPA
-ENSPPR ENSPPY ENSPSI ENSPTI ENSPTR ENSPVA ENSRBI ENSRNO ENSRRO ENSSAR
-ENSSBO ENSSCE ENSSHA ENSSSC ENSSTO ENSTBE ENSTGU ENSTNI ENSTRU ENSTSY
-ENSTTR ENSVPA ENSXET ENSXMA FB MGP_129S1SvImJ_ MGP_AJ_ MGP_AKRJ_
-MGP_BALBcJ_ MGP_C3HHeJ_ MGP_C57BL6NJ_ MGP_CAROLIEiJ_ MGP_CASTEiJ_
-MGP_CBAJ_ MGP_DBA2J_ MGP_FVBNJ_ MGP_LPJ_ MGP_NODShiLtJ_ MGP_NZOHlLtJ_
-MGP_PWKPhJ_ MGP_PahariEiJ_ MGP_SPRETEiJ_ MGP_WSBEiJ_""".split())
-_ensembl_feature_types_re = r"E|FM|G|GT|P|R|T"
-_ensembl_re = r"^(?:{})(?:{}){}$".format(
-    _ensembl_species_prefixes, _ensembl_feature_types_re, r"\d{11}(?:\.\d+)?")
-
-ac_namespace_regexps = {
-    # https://uswest.ensembl.org/info/genome/stable_ids/prefixes.html
-    # [species prefix][feature type prefix][a unique eleven digit number]
-    # N.B. The regexp at http://identifiers.org/ensembl appears broken:
-    # 1) Human only; 2) escaped backslashes (\\d rather than \d).
-    _ensembl_re: "Ensembl",
-
-    # http://identifiers.org/insdc/
-    # P12345, a UniProtKB accession matches the miriam regexp but shouldn't (I think)
-    r"^([A-Z]\d{5}|[A-Z]{2}\d{6}|[A-Z]{4}\d{8}|[A-J][A-Z]{2}\d{5})(\.\d+)?$":
-    "INSDC",
-
-    # http://identifiers.org/refseq/
-    # https://www.ncbi.nlm.nih.gov/books/NBK21091/table/ch18.T.refseq_accession_numbers_and_mole/
-    r"^((AC|AP|NC|NG|NM|NP|NR|NT|NW|XM|XP|XR|YP|ZP)_\d+|(NZ\_[A-Z]{4}\d+))(\.\d+)?$":
-    "RefSeq",
-
-    # UniProtKB
-    # http://identifiers.org/uniprot/
-    # https://www.uniprot.org/help/accession_numbers
-    r"^(?:[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2})$":
-    "UniProtKB",
-}
-
-ac_namespace_regexps = {re.compile(k): v for k, v in iteritems(ac_namespace_regexps)}
-
-
-def chr22XY(c):
-    """force to name from 1..22, 23, 24, X, Y, M 
-    to in chr1..chr22, chrX, chrY, chrM
-    str or ints accepted
-
-    >>> chr22XY('1')
-    'chr1'
-    >>> chr22XY(1)
-    'chr1'
-    >>> chr22XY('chr1')
-    'chr1'
-    >>> chr22XY(23)
-    'chrX'
-    >>> chr22XY(24)
-    'chrY'
-    >>> chr22XY("X")
-    'chrX'
-    >>> chr22XY("23")
-    'chrX'
-    >>> chr22XY("M")
-    'chrM'
-
-    """
-    c = str(c)
-    if c[0:3] == 'chr':
-        c = c[3:]
-    if c == '23':
-        c = 'X'
-    if c == '24':
-        c = 'Y'
-    return 'chr' + c
-
-
-def infer_namespace(ac):
-    """Infer the single namespace of the given accession
-
-    This function is convenience wrapper around infer_namespaces().
-    Returns:
-      * None if no namespaces are inferred
-      * The (single) namespace if only one namespace is inferred
-      * Raises an exception if more than one namespace is inferred
-
-    >>> infer_namespace("ENST00000530893.6")
-    'Ensembl'
-
-    >>> infer_namespace("NM_01234.5")
-    'RefSeq'
-
-    >>> infer_namespace("A2BC19")
-    'UniProtKB'
-
-    >>> infer_namespace("P12345")
-    Traceback (most recent call last):
-    ...
-    bioutils.exceptions.BioutilsError: Multiple namespaces possible for P12345
-
-    >>> infer_namespace("BOGUS99") is None
-    True
-
-    """
-
-    namespaces = infer_namespaces(ac)
-    if not namespaces:
-        return None
-    if len(namespaces) > 1:
-        raise BioutilsError("Multiple namespaces possible for {}".format(ac))
-    return namespaces[0]
-
-
-def infer_namespaces(ac):
-    """infer possible namespaces of given accession based on syntax
-    Always returns a list, possibly empty
-
-    >>> infer_namespaces("ENST00000530893.6")
-    ['Ensembl']
-    >>> infer_namespaces("ENST00000530893")
-    ['Ensembl']
-    >>> infer_namespaces("ENSQ00000530893")
-    []
-    >>> infer_namespaces("NM_01234")
-    ['RefSeq']
-    >>> infer_namespaces("NM_01234.5")
-    ['RefSeq']
-    >>> infer_namespaces("NQ_01234.5")
-    []
-    >>> infer_namespaces("A2BC19")
-    ['UniProtKB']
-    >>> sorted(infer_namespaces("P12345"))
-    ['INSDC', 'UniProtKB']
-    >>> infer_namespaces("A0A022YWF9")
-    ['UniProtKB']
-
-
-    """
-    return [v for k, v in iteritems(ac_namespace_regexps) if k.match(ac)]
-
 
 def prepend_chr(chr):
     """prefix chr with 'chr' if not present
@@ -184,15 +38,46 @@ def strip_chr(chr):
     return chr[3:] if chr[0:3] == 'chr' else chr
 
 
+def chr22XY(c):
+    """force to name from 1..22, 23, 24, X, Y, M 
+    to in chr1..chr22, chrX, chrY, chrM
+    str or ints accepted
+
+    >>> chr22XY('1')
+    'chr1'
+    >>> chr22XY(1)
+    'chr1'
+    >>> chr22XY('chr1')
+    'chr1'
+    >>> chr22XY(23)
+    'chrX'
+    >>> chr22XY(24)
+    'chrY'
+    >>> chr22XY("X")
+    'chrX'
+    >>> chr22XY("23")
+    'chrX'
+    >>> chr22XY("M")
+    'chrM'
+
+    """
+    c = str(c)
+    if c[0:3] == 'chr':
+        c = c[3:]
+    if c == '23': c = 'X'
+    if c == '24': c = 'Y'
+    return 'chr' + c
+
+
 ## <LICENSE>
 ## Copyright 2014 Bioutils Contributors (https://bitbucket.org/biocommons/bioutils)
-##
+## 
 ## Licensed under the Apache License, Version 2.0 (the "License");
 ## you may not use this file except in compliance with the License.
 ## You may obtain a copy of the License at
-##
+## 
 ##     http://www.apache.org/licenses/LICENSE-2.0
-##
+## 
 ## Unless required by applicable law or agreed to in writing, software
 ## distributed under the License is distributed on an "AS IS" BASIS,
 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/bioutils/exceptions.py b/bioutils/exceptions.py
deleted file mode 100644
index f6c5aaa..0000000
--- a/bioutils/exceptions.py
+++ /dev/null
@@ -1,3 +0,0 @@
-class BioutilsError(Exception):
-    """Root exception for all bioutils exceptions"""
-    pass
diff --git a/bioutils/seqfetcher.py b/bioutils/seqfetcher.py
index 3658542..919997f 100644
--- a/bioutils/seqfetcher.py
+++ b/bioutils/seqfetcher.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
 
 import logging
 import re
+import os
 
 import requests
 
@@ -203,12 +204,29 @@ def _fetch_seq_ncbi(ac, start_i=None, end_i=None):
 
     url += "&tool={tool}&email={email}".format(tool=ncbi_tool, email=ncbi_email)
 
+    url = _add_eutils_api_key(url)
+
     resp = requests.get(url)
     resp.raise_for_status()
     seq = ''.join(resp.text.splitlines()[1:])
     return seq
 
 
+
+def _add_eutils_api_key(url):
+    """Adds eutils api key to the query
+
+    :param url: eutils url with a query string
+    :return: url with api_key parameter set to the value of environment
+    variable 'ncbi_api_key' if available
+    """
+    apikey = os.environ.get('ncbi_api_key')
+    if apikey:
+        url += '&api_key={apikey}'.format(apikey=apikey)
+    return url
+
+
+
 # So that I don't forget why I didn't use ebi too:
 # $ curl 'http://www.ebi.ac.uk/ena/data/view/AM407889.1&display=fasta'
 # >ENA|AM407889|AM407889.2 Medicago sativa partial mRNA ...
diff --git a/bioutils/sequences.py b/bioutils/sequences.py
index 836e04f..f216464 100644
--- a/bioutils/sequences.py
+++ b/bioutils/sequences.py
@@ -3,14 +3,10 @@
 
 from __future__ import absolute_import, division, print_function, unicode_literals
 
-import logging
-import re
-
 import six
+import re
 
 
-_logger = logging.getLogger(__name__)
-
 aa3_to_aa1_lut = {
     "Ala": "A",
     "Arg": "R",
@@ -36,7 +32,6 @@ aa3_to_aa1_lut = {
     "Ter": "*",
     "Sec": "",
 }
-
 aa1_to_aa3_lut = {v: k for k, v in six.iteritems(aa3_to_aa1_lut)}
 
 
@@ -137,33 +132,6 @@ def complement(seq):
     return seq.translate(complement_transtable)
 
 
-def elide_sequence(s, flank=5, elision="..."):
-    """trim a sequence to include the left and right flanking sequences of
-    size `flank`, with the intervening sequence elided by `elision`.
-
-    >>> elide_sequence("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
-    'ABCDE...VWXYZ'
-
-    >>> elide_sequence("ABCDEFGHIJKLMNOPQRSTUVWXYZ", flank=3)
-    'ABC...XYZ'
-
-    >>> elide_sequence("ABCDEFGHIJKLMNOPQRSTUVWXYZ", elision="..")
-    'ABCDE..VWXYZ'
-
-    >>> elide_sequence("ABCDEFGHIJKLMNOPQRSTUVWXYZ", flank=12)
-    'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-
-    >>> elide_sequence("ABCDEFGHIJKLMNOPQRSTUVWXYZ", flank=12, elision=".")
-    'ABCDEFGHIJKL.OPQRSTUVWXYZ'
-
-    """
-
-    elided_sequence_len = flank + flank + len(elision)
-    if len(s) <= elided_sequence_len:
-        return s
-    return s[:flank] + elision + s[-flank:]
-
-
 def looks_like_aa3_p(seq):
     """string looks like a 3-letter AA string"""
     return (seq is not None and (len(seq) % 3 == 0) and
@@ -185,18 +153,14 @@ def normalize_sequence(seq):
     >>> normalize_sequence("ACGT1")
     Traceback (most recent call last):
     ...
-    RuntimeError: Normalized sequence contains non-alphabetic characters
+    RuntimeError: normalized sequence contains non-alphabetic characters
 
     """
 
     assert isinstance(seq, six.text_type)
     nseq = re.sub("[\s\*]", "", seq).upper()
-    m = re.search("[^A-Z]", nseq)
-    if m:
-        _logger.debug("Original sequence: " + seq)
-        _logger.debug("Normalized sequence: " + nseq)
-        _logger.debug("First non-[A-Z] at {}".format(m.start()))
-        raise RuntimeError("Normalized sequence contains non-alphabetic characters")
+    if re.search("[^A-Z]", nseq):
+        raise RuntimeError("normalized sequence contains non-alphabetic characters")
     return nseq
 
 
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 0000000..fdd64ec
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,3 @@
+machine:
+  post:
+    - pyenv global 2.7.12 3.5.3

Add support for eutils' api_key parameter
Utils will require an api_key starting May 1, 2018.
We should support adding an api_key as an environment variable.
diff --git a/tests/test_seqfetcher.py b/tests/test_seqfetcher.py
index 4d44847..6154fa6 100644
--- a/tests/test_seqfetcher.py
+++ b/tests/test_seqfetcher.py
@@ -1,7 +1,8 @@
 import pytest
 import vcr
+import os
 
-from bioutils.seqfetcher import fetch_seq, _fetch_seq_ensembl, _fetch_seq_ncbi
+from bioutils.seqfetcher import fetch_seq, _fetch_seq_ensembl, _fetch_seq_ncbi, _add_eutils_api_key
 
 
 @vcr.use_cassette
@@ -21,7 +22,20 @@ def test_fetch_seq():
     assert 'TTTATTTATTTTAGATACTTATCTC' == fetch_seq('KB663603.1', 0, 25)
     assert 'CGCCTCCCTTCCCCCTCCCCGCCCG' == fetch_seq('ENST00000288602', 0, 25)
     assert 'MAALSGGGGGGAEPGQALFNGDMEP' == fetch_seq('ENSP00000288602', 0, 25)
-    
+
+
+def test_add_eutils_api_key():
+    try:
+        url = 'http://test.com?boo=bar'
+        assert _add_eutils_api_key(url) == url
+        os.environ['ncbi_api_key'] = 'test-api-key'
+        assert _add_eutils_api_key(url) == url + '&api_key=test-api-key'
+    finally:
+        try:
+            os.environ.pop('ncbi_api_key')
+        except KeyError:
+            pass
+
 
 def test_fetch_seq_errors():
     # Traceback (most recent call last):

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
3.5
etc/install.reqs etc/develop.reqs
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
backcall==0.2.0
-e git+https://github.com/biocommons/bioutils.git@b9d435b7815a1ffc849cd3980e408bbed53f6bcb#egg=bioutils
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
decorator==5.1.1
distlib==0.3.9
filelock==3.4.1
flake8==5.0.4
idna==3.10
importlib-metadata==4.2.0
importlib-resources==5.4.0
iniconfig==1.1.1
ipython==7.16.3
ipython-genutils==0.2.0
jedi==0.17.2
mccabe==0.7.0
multidict==5.2.0
packaging==21.3
parso==0.7.1
pexpect==4.9.0
pickleshare==0.7.5
platformdirs==2.4.0
pluggy==1.0.0
prompt-toolkit==3.0.36
ptyprocess==0.7.0
py==1.11.0
pycodestyle==2.9.1
pyflakes==2.5.0
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
PyYAML==6.0.1
requests==2.27.1
six==1.17.0
toml==0.10.2
tomli==1.2.3
tox==3.28.0
traitlets==4.3.3
typing_extensions==4.1.1
urllib3==1.26.20
vcrpy==4.1.1
virtualenv==20.16.2
wcwidth==0.2.13
wrapt==1.16.0
yapf==0.32.0
yarl==1.7.2
zipp==3.6.0

name: bioutils
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - backcall==0.2.0
      - charset-normalizer==2.0.12
      - coverage==6.2
      - decorator==5.1.1
      - distlib==0.3.9
      - filelock==3.4.1
      - flake8==5.0.4
      - idna==3.10
      - importlib-metadata==4.2.0
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - ipython==7.16.3
      - ipython-genutils==0.2.0
      - jedi==0.17.2
      - mccabe==0.7.0
      - multidict==5.2.0
      - packaging==21.3
      - parso==0.7.1
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - prompt-toolkit==3.0.36
      - ptyprocess==0.7.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pyyaml==6.0.1
      - requests==2.27.1
      - six==1.17.0
      - toml==0.10.2
      - tomli==1.2.3
      - tox==3.28.0
      - traitlets==4.3.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - vcrpy==4.1.1
      - virtualenv==20.16.2
      - wcwidth==0.2.13
      - wrapt==1.16.0
      - yapf==0.32.0
      - yarl==1.7.2
      - zipp==3.6.0
prefix: /opt/conda/envs/bioutils

tests/test_seqfetcher.py::test_fetch_seq tests/test_seqfetcher.py::test_add_eutils_api_key tests/test_seqfetcher.py::test_fetch_seq_errors
README.rst Makefile bioutils/sequences.py .vscode/settings.json bioutils/exceptions.py bioutils/seqfetcher.py .gitignore bioutils/accessions.py circle.yml
README.rst Makefile bioutils/sequences.py .vscode/settings.json bioutils/exceptions.py bioutils/seqfetcher.py .gitignore bioutils/accessions.py circle.yml