First seen time

You can fetch the first seen time for any txid using mempool.space API's.

This script will handle both confirmed and unconfirmed transactions.

Regular Confirmed

python txfirstseen.py 1a13c435cb5a423abe56b711b33e753d74fe291da52a4d77bc7d58ebe3914e0c

Regular confirmed transaction

{
  "txid": "1a13c435cb5a423abe56b711b33e753d74fe291da52a4d77bc7d58ebe3914e0c",
  "seen": {
    "unix": 1725539361,
    "human": "2024-09-05 12:29:21 UTC"
  },
  "conf": {
    "unix": 1725539528,
    "human": "2024-09-05 12:32:08 UTC"
  }
}

response

Confirmed but not expected in block

python txfirstseen.py 174fbe5733f3d3669e03a1b26d269b67b226413c1e5155ef16e1d000ab9fcd98
{
  "txid": "174fbe5733f3d3669e03a1b26d269b67b226413c1e5155ef16e1d000ab9fcd98",
  "seen": {
    "unix": 1726890532,
    "human": "2024-09-21 03:48:52 UTC"
  },
  "conf": {
    "unix": 1726905651,
    "human": "2024-09-21 08:00:51 UTC"
  }
}

Old Confirmed

python txfirstseen.py 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098

Old confirmed transaction before block audit data

{
  "txid": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
  "seen": "unknown",
  "conf": {
    "unix": 1231469665,
    "human": "2009-01-09 02:54:25 UTC"
  }
}

response

Unconfirmed

python txfirstseen.py 807b3097009d51db785b1aa2c4e82bd9a4f26909e19ed5345931b60ecefea1bf

Unconfirmed transaction

{
  "txid": "807b3097009d51db785b1aa2c4e82bd9a4f26909e19ed5345931b60ecefea1bf",
  "seen": {
    "unix": 1727098078,
    "human": "2024-09-23 13:27:58 UTC"
  },
  "conf": null
}

response

Code

import sys
import json
from datetime import datetime, timezone
import requests 

def get_transaction_info(txid):
    tx_url = f"https://mempool.space/api/tx/{txid}"
    try:
        response = requests.get(tx_url)
        response.raise_for_status()
        tx_data = response.json()
        
        first_seen = "unknown"
        confirmation_time = None
        
        if tx_data['status']['confirmed']:
            block_hash = tx_data['status']['block_hash']
            confirmation_time = tx_data['status']['block_time']
            
            # Try to get audit data for first seen time
            try:
                block_url = f"https://mempool.space/api/v1/block/{block_hash}/tx/{txid}/audit"
                block_response = requests.get(block_url)
                block_response.raise_for_status()
                block_data = block_response.json()
                
                if 'firstSeen' in block_data:
                    first_seen = block_data['firstSeen']
                else:
                    # If audit data doesn't have firstSeen, try block summary
                    summary_url = f"https://mempool.space/api/v1/block/{block_hash}/summary"
                    summary_response = requests.get(summary_url)
                    summary_response.raise_for_status()
                    summary_data = summary_response.json()
                    
                    for tx in summary_data:
                        if tx['txid'] == txid:
                            first_seen = tx['time']
                            break
            except requests.exceptions.RequestException as e:
                print(f"Error fetching additional data: {e}")
                # If both attempts fail, first seen remains "unknown"
        else:
            # Transaction is unconfirmed, use transaction-times API for first seen
            times_url = f"https://mempool.space/api/v1/transaction-times?txId[]={txid}"
            times_response = requests.get(times_url)
            times_response.raise_for_status()
            times_data = times_response.json()
            
            if isinstance(times_data, dict) and txid in times_data:
                first_seen = times_data[txid]
            elif isinstance(times_data, list) and len(times_data) > 0:
                first_seen = times_data[0][0] if isinstance(times_data[0], list) else times_data[0]
            else:
                print(f"Unexpected response format from transaction-times API: {times_data}")

        return first_seen, confirmation_time

    except requests.exceptions.RequestException as e:
        print(f"Error occurred while fetching data: {e}")
        return None, None

def format_time(timestamp):
    if timestamp is None:
        return None
    if timestamp == "unknown":
        return "unknown"
    unix_time = int(timestamp)
    human_readable = datetime.fromtimestamp(unix_time, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S %Z')
    return {"unix": unix_time, "human": human_readable}

def main():
    if len(sys.argv) != 2:
        print("Usage: python script.py <txid>")
        sys.exit(1)
    
    txid = sys.argv[1]
    first_seen, confirmation_time = get_transaction_info(txid)
    
    if first_seen is not None or confirmation_time is not None:
        result = {
            "txid": txid,
            "seen": format_time(first_seen),
            "conf": format_time(confirmation_time)
        }
        print(json.dumps(result, indent=2))
    else:
        print(json.dumps({"error": f"Unable to retrieve information for transaction ID: {txid}"}, indent=2))

if __name__ == "__main__":
    main()