diff --git a/geomagio/processing/copy_absolutes.py b/geomagio/processing/copy_absolutes.py
index 86d0748c591c375656d630f674806ac1bd2f1752..d1318159ae57f5f7159aa3bbf51a455698d0c6b8 100644
--- a/geomagio/processing/copy_absolutes.py
+++ b/geomagio/processing/copy_absolutes.py
@@ -38,7 +38,7 @@ def copy_absolutes(
         show_default="environment variable GITLAB_API_TOKEN",
     ),
     metadata_url: str = typer.Option(
-        default="https://geomag.usgs.gov/ws/secure",
+        default="https://geomag.usgs.gov/ws/secure/metadata",
         help="URL to metadata web service",
         metavar="URL",
     ),
@@ -66,8 +66,10 @@ def copy_absolutes(
     """Copy absolutes from the web absolutes service OR residual spreadsheets into the metadata service."""
     if factory.value == ResidualFactory.WEB_ABSOLUTES:
         factory = WebAbsolutesFactory(url=web_absolutes_url)
+        created_by_label = "webabsolutes"
     else:
         factory = SpreadsheetAbsolutesFactory(base_directory=directory)
+        created_by_label = "spreadsheet"
 
     readings = get_readings(
         factory=factory,
@@ -89,10 +91,14 @@ def copy_absolutes(
         iterable=readings, label="Uploading to metadata service"
     ) as progressbar:
         for reading in progressbar:
-            upload_reading(factory=metadata_factory, reading=reading)
+            upload_reading(
+                factory=metadata_factory,
+                reading=reading,
+                created_by_label=created_by_label,
+            )
 
 
-def create_reading_metadata(reading: Reading) -> Metadata:
+def create_reading_metadata(reading: Reading, created_by_label: str) -> Metadata:
     """Create reading metadata object.
 
     Parameters
@@ -107,7 +113,7 @@ def create_reading_metadata(reading: Reading) -> Metadata:
     measurement_times = [m.time for m in reading.measurements if m.time]
     metadata = Metadata(
         category=MetadataCategory.READING,
-        created_by=reading.metadata.get("observer", "copy_absolutes"),
+        created_by=(created_by_label),
         endtime=max(measurement_times),
         metadata=json.loads(reading.json()),
         network="NT",
@@ -157,7 +163,9 @@ def main() -> None:
     typer.run(copy_absolutes)
 
 
-def upload_reading(factory: MetadataFactory, reading: Reading) -> Metadata:
+def upload_reading(
+    factory: MetadataFactory, reading: Reading, created_by_label: str
+) -> Metadata:
     """Upload reading to metadata service
 
     Parameters
@@ -171,7 +179,9 @@ def upload_reading(factory: MetadataFactory, reading: Reading) -> Metadata:
     -------
     created metadata object
     """
-    metadata = create_reading_metadata(reading=reading)
+    metadata = create_reading_metadata(
+        reading=reading, created_by_label=created_by_label
+    )
     # TODO: should this check if metadata was already uploaded?
     # TODO: should that check occur before calling this method?
     return factory.create_metadata(metadata=metadata)
diff --git a/geomagio/processing/copy_observatory.py b/geomagio/processing/copy_observatory.py
new file mode 100644
index 0000000000000000000000000000000000000000..331418eef7f9baab1e8e7645c696d07c4f83f5c0
--- /dev/null
+++ b/geomagio/processing/copy_observatory.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Thu Jul  7 11:05:19 2022
+
+@author: awernle
+"""
+import os
+
+import typer
+
+from geomagio.api.ws.Observatory import OBSERVATORIES
+from ..metadata import Metadata, MetadataFactory, MetadataCategory
+
+
+def copy_observatory(
+    observatory: str = typer.Option(..., help="Observatory code"),
+    metadata_token: str = typer.Option(
+        default=os.getenv("GITLAB_API_TOKEN"),
+        help="Token for metadata web service.",
+        metavar="TOKEN",
+        show_default="environment variable GITLAB_API_TOKEN",
+    ),
+    metadata_url: str = typer.Option(
+        default="https://staging-geomag.cr.usgs.gov/ws/secure/metadata",
+        help="URL to metadata web service",
+        metavar="URL",
+    ),
+):
+    """Copy observatory metadata into the metadata webservice."""
+
+    created_by_label = "copy_observatory.py"
+
+    observatory_metadata = get_observatory_metadata(
+        observatory=observatory,
+    )
+
+    # confirm whether or not to copy observatory metadata
+    typer.confirm(f"Are you sure you want to copy observatory metadata?", abort=True)
+    print("Copying over observatory metadata")
+
+    # write observatory metadata to metadata service
+    metadata_factory = MetadataFactory(token=metadata_token, url=metadata_url)
+    with typer.progressbar(
+        iterable=observatory_metadata, label="Uploading to metadata service"
+    ) as progressbar:
+        for observatory in progressbar:
+            upload_observatory_metadata(
+                factory=metadata_factory,
+                observatory=observatory,
+                created_by_label=created_by_label,
+            )
+
+
+def create_observatory_metadata(observatory, created_by_label: str) -> Metadata:
+    """Create observatory metadata object.
+
+    Parameters
+    ----------
+    observatory:
+        observatory to convert to metadata.
+
+    Returns
+    -------
+    metadata object for observatory
+    """
+    # define network based on observatory
+    network = "NT"
+    if observatory.agency == "USGS":
+        network = "NT"
+    # rest alphabetical by agency
+    elif observatory.agency == "BGS":
+        network = "GB"
+    elif observatory.agency == "GSC":
+        network = "C2"
+    elif observatory.agency == "JMA":
+        network = "JP"
+    elif observatory.agency == "SANSA":
+        network = "AF"
+
+    metadata = Metadata(
+        category=MetadataCategory.OBSERVATORY,
+        created_by=created_by_label,
+        network=network,
+        station=observatory.id,
+        metadata=observatory.dict(),
+    )
+    return metadata
+
+
+def get_observatory_metadata(
+    observatory: str,
+):
+    """Get metadata from OBSERVATORIES list.
+
+    Parameters
+    ----------
+    observatory
+        search observatory
+
+    Returns
+    -------
+    observatory metadata
+    """
+    observatories = []
+    # add observatories
+    for observatory_ in OBSERVATORIES:
+        if observatory == observatory_.id:
+            observatories.append(observatory_)
+    print(observatories)
+    return observatories
+
+
+def main() -> None:
+    """Entrypoint for copy observatory method."""
+    # for one command, can use typer.run
+    typer.run(copy_observatory)
+
+
+def upload_observatory_metadata(
+    factory: MetadataFactory, observatory, created_by_label: str
+) -> Metadata:
+    """Upload observatory to metadata service
+
+    Parameters
+    ----------
+    factory
+        factory configured for metadata service
+    observatory
+        observatory to upload
+
+    Returns
+    -------
+    created metadata object
+    """
+    metadata = create_observatory_metadata(
+        observatory=observatory, created_by_label=created_by_label
+    )
+    return factory.create_metadata(metadata=metadata)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/pyproject.toml b/pyproject.toml
index 07f76bfa832c9c9e8f63a3789586f208d16b7094..c00c7f89e0d48e48652498f64c3689781114ccab 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -76,7 +76,7 @@ magproc-prepfiles = "geomagio.processing.magproc:main"
 make-cal = "geomagio.processing.make_cal:main"
 geomag-filter = "geomagio.processing.filters:main"
 copy-absolutes = "geomagio.processing.copy_absolutes:main"
-
+copy-observatory = "geomagio.processing.copy_observatory:main"
 
 [tool.poe.tasks]
 # e.g. "poetry run poe lint"