Skip to content
University of Maryland
CTRL K
    GitHub Atom Feed
    Give Now

    DRUM

    The Digital Repository at the University of Maryland (DRUM) collects, preserves, and provides public access to the scholarly output of the university. Faculty and researchers can upload research products for rapid dissemination, global visibility and impact, and long-term preservation.

    OAI-PMH

    API Description: OAI-PMH

    Endpoint: https://api.drum.lib.umd.edu/server/oai/request

    Example:

    #!/bin/bash
    
    curl "https://api.drum.lib.umd.edu/server/oai/request?verb=Identify"
    
    curl "https://api.drum.lib.umd.edu/server/oai/request?verb=ListSets"
    
    curl "https://api.drum.lib.umd.edu/server/oai/request?verb=ListMetadataFormats"

    Additional Examples:

    OpenSearch

    API Description: OpenSearch

    Endpoint: https://api.drum.lib.umd.edu/server/opensearch/search

    Example: drum-search.py

    drum-search.py
    #!/usr/bin/env -S uv run --script
    # /// script
    # requires-python = ">=3.12"
    # dependencies = []
    # ///
    
    import socket
    import ssl
    import urllib.request
    import sys
    from urllib.error import HTTPError, URLError
    from xml.etree import ElementTree
    
    # Search DRUM using OpenSearch
    
    ENDPOINT = 'https://api.drum.lib.umd.edu/server/opensearch/search'
    
    ATOM = '{http://www.w3.org/2005/Atom}'
    OPENSEARCH = '{http://a9.com/-/spec/opensearch/1.1/}'
    
    # A host name that does not resolve, or a certificate that will not verify,
    # is a settled fact about the URL rather than a passing condition: it means
    # this example is pointed somewhere that no longer answers for it. Every other
    # connection failure -- refused, reset, timed out -- may succeed on a retry.
    PERMANENT_FAILURES = (socket.gaierror, ssl.SSLCertVerificationError)
    
    
    def search(**params):
        ''' Search DRUM using OpenSearch, using named parameters '''
    
        # Build the URL
        params['rpp'] = 5  # get first 5 results
        search_url = ENDPOINT + '?' + urllib.parse.urlencode(params)
    
        print('\n========================')
        print(f'Search URL: {search_url}')
    
        # Get search results as parsed XML
        try:
            with urllib.request.urlopen(search_url) as request:
                atom = ElementTree.parse(request).getroot()
        except HTTPError as error:
            print(f'{ENDPOINT} returned HTTP {error.code}: {error.reason}', file=sys.stderr)
            # 75 = EX_TEMPFAIL: the service is reachable but cannot serve right now.
            # A 4xx means this request is no longer valid, which is this example's problem.
            sys.exit(75 if error.code >= 500 or error.code == 429 else 1)
        except URLError as error:
            # Nothing answered, so the request was never judged. What stopped it
            # decides whose problem it is.
            print(f'Could not reach {ENDPOINT}: {error.reason}', file=sys.stderr)
            sys.exit(1 if isinstance(error.reason, PERMANENT_FAILURES) else 75)
        except ElementTree.ParseError:
            print(f'{ENDPOINT} did not return XML', file=sys.stderr)
            # A body that is not XML means the API changed under this example.
            sys.exit(1)
    
        # A search that matches nothing is a legitimate answer, so check for the
        # OpenSearch envelope rather than for entries: an Atom feed carrying
        # opensearch:totalResults is what says the response was understood.
        if atom.tag != ATOM + 'feed' or atom.find(OPENSEARCH + 'totalResults') is None:
            print(f'{search_url} did not return an OpenSearch Atom feed', file=sys.stderr)
            sys.exit(1)
    
        # Iterate over the returned items
        for element in atom.findall(ATOM + 'entry'):
    
            # Extract Item information
            title = element.find(ATOM + 'title').text
            handle_url = element.find(ATOM + 'link').get('href')
            author = element.find(f'{ATOM}author/{ATOM}name').text
    
            print('----')
            print(f'Title:      {title}')
            print(f'Author:     {author}')
            print(f'Handle URL: {handle_url}')
    
    
    # phrase is "Black Lives Matter"
    search(query='"Black Lives Matter"')
    
    
    # advisor is smela
    # collection is http://hdl.handle.net/1903/2795 (Mechanical Engineering ETDs)
    search(query='advisor:smela', scope='a96d44b7-57d2-46ed-80e9-98f316a82a19')
    
    
    # community is http://hdl.handle.net/1903/2278 (Library Staff Research Works)
    search(query='*:*', scope='14dd3089-86f9-4bac-949f-347e0c637984')
    

    Run this example with uv — no download or setup required:

    uv run https://opendata.lib.umd.edu/code/drum-search.py

    DSpace REST API

    API Description: DSpace REST API

    Endpoint: https://api.drum.lib.umd.edu/server/api

    The endpoint is explorable using the HAL Browser at https://api.drum.lib.umd.edu/server.

    Example:

    drum-api.py
    #!/usr/bin/env -S uv run --script
    # /// script
    # requires-python = ">=3.12"
    # dependencies = []
    # ///
    
    import urllib.request
    import json
    import socket
    import ssl
    import sys
    from urllib.error import HTTPError, URLError
    
    # Get a list of items from DRUM using the REST API
    
    ENDPOINT = 'https://api.drum.lib.umd.edu/server/api'
    
    # A host name that does not resolve, or a certificate that will not verify,
    # is a settled fact about the URL rather than a passing condition: it means
    # this example is pointed somewhere that no longer answers for it. Every other
    # connection failure -- refused, reset, timed out -- may succeed on a retry.
    PERMANENT_FAILURES = (socket.gaierror, ssl.SSLCertVerificationError)
    
    items_url = ENDPOINT + '/discover/browses/title/items'
    
    # Get JSON results
    try:
        with urllib.request.urlopen(items_url) as request:
            response = json.loads(request.read())
    except HTTPError as error:
        print(f'{items_url} returned HTTP {error.code}: {error.reason}', file=sys.stderr)
        # 75 = EX_TEMPFAIL: the service is reachable but cannot serve right now.
        # A 4xx means this request is no longer valid, which is this example's problem.
        sys.exit(75 if error.code >= 500 or error.code == 429 else 1)
    except URLError as error:
        # Nothing answered, so the request was never judged. What stopped it
        # decides whose problem it is.
        print(f'Could not reach {items_url}: {error.reason}', file=sys.stderr)
        sys.exit(1 if isinstance(error.reason, PERMANENT_FAILURES) else 75)
    except json.JSONDecodeError:
        print(f'{items_url} did not return JSON', file=sys.stderr)
        # A body that is not JSON means the API changed under this example.
        sys.exit(1)
    
    # Iterate over the returned items
    for item in response['_embedded']['items']:
        md = item['metadata']
        if 'dc.identifier.uri' in md:
            link = "; ".join(entry['value'] for entry in md['dc.identifier.uri'])
        else:
            link = "n/a"
        if 'dc.title' in md:
            title = "; ".join(entry['value'] for entry in md['dc.title'])
        else:
            title = "n/a"
    
        print('----')
        print(f'Title: {title}')
        print(f'Link:  {link}')
    

    Run this example with uv — no download or setup required:

    uv run https://opendata.lib.umd.edu/code/drum-api.py

    Additional Example:

    JSON-LD

    Format Description: JSON-LD

    You can extract schema.org DataSet structured data, encoded using JSON-LD, from HTML pages in our UMD Data Community.

    Example: