diff --git a/docs/guide/serializers.rst b/docs/guide/serializers.rst
index 722da38..bb9f64b 100644
--- a/docs/guide/serializers.rst
+++ b/docs/guide/serializers.rst
@@ -117,9 +117,14 @@ All field classes accept this set of arguments:
 
 * **validators** *(list, optional):* list of validator callables.
 
-* **many** *(bool, optional)* set to True if field is in fact a list
+* **many** *(bool, optional):* set to True if field is in fact a list
   of given type objects
 
+* **read_only** *(bool):* True if field is read-only and cannot be set/modified
+  via POST, PUT, or PATCH requests.
+
+* **write_only** *(bool):* True if field is write-only and cannot be retrieved
+  via GET requests.
 
 .. note::
 
diff --git a/src/graceful/fields.py b/src/graceful/fields.py
index b306aa2..d7fbf89 100644
--- a/src/graceful/fields.py
+++ b/src/graceful/fields.py
@@ -35,10 +35,15 @@ class BaseField:
         validators (list): list of validator callables.
 
         many (bool): set to True if field is in fact a list of given type
-           objects
+           objects.
 
-        read_only (bool): True if field is read only and cannot be set/modified
-            by POST and PUT requests
+        read_only (bool): True if field is read-only and cannot be set/modified
+            via POST, PUT, or PATCH requests.
+
+        write_only (bool): True if field is write-only and cannot be retrieved
+            via GET requests.
+
+            .. versionadded:: 0.5.0
 
     Example:
 
@@ -77,6 +82,7 @@ class BaseField:
             validators=None,
             many=False,
             read_only=False,
+            write_only=False,
     ):
         """Initialize field definition."""
         self.label = label
@@ -85,6 +91,12 @@ class BaseField:
         self.validators = validators or []
         self.many = many
         self.read_only = read_only
+        self.write_only = write_only
+
+        if self.write_only and self.read_only:
+            raise ValueError(
+                "Field cannot be read-only and write-only at the same time."
+            )
 
     def from_representation(self, data):
         """Convert representation value to internal value.
@@ -142,6 +154,7 @@ class BaseField:
             'type': "list of {}".format(self.type) if self.many else self.type,
             'spec': self.spec,
             'read_only': self.read_only,
+            'write_only': self.write_only,
         }
         description.update(kwargs)
         return description
diff --git a/src/graceful/serializers.py b/src/graceful/serializers.py
index e83a76a..6a914d0 100644
--- a/src/graceful/serializers.py
+++ b/src/graceful/serializers.py
@@ -118,6 +118,9 @@ class BaseSerializer(metaclass=MetaSerializer):
         representation = {}
 
         for name, field in self.fields.items():
+            if field.write_only:
+                continue
+
             # note fields do not know their names in source representation
             # but may know what attribute they target from source object
             attribute = self.get_attribute(obj, field.source or name)

Add "write_only" fields
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 888fb87..4d30e78 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -21,6 +21,15 @@ def test_base_field_implementation_hooks():
         field.from_representation(None)
 
 
+def test_base_field_write_or_read_only_not_both():
+
+    assert BaseField("Read only", read_only=True).write_only is False
+    assert BaseField("Write only", write_only=True).read_only is False
+
+    with pytest.raises(ValueError):
+        BaseField("Both", read_only=True, write_only=True)
+
+
 def test_base_field_describe():
     class SomeField(BaseField):
         type = "anything"
@@ -33,7 +42,8 @@ def test_base_field_describe():
         'details': "bar",
         'type': "anything",
         'spec': None,
-        'read_only': False
+        'read_only': False,
+        'write_only': False
     }
 
     # test extending descriptions by call with kwargs
diff --git a/tests/test_serializers.py b/tests/test_serializers.py
index 5e7b353..0a10553 100644
--- a/tests/test_serializers.py
+++ b/tests/test_serializers.py
@@ -5,6 +5,8 @@ we test how whole framework for defining new serializers works.
 """
 import pytest
 
+import graceful
+from graceful.errors import DeserializationError
 from graceful.fields import BaseField, StringField
 from graceful.serializers import BaseSerializer
 
@@ -197,6 +199,56 @@ def test_serializer_get_attribute():
     assert serializer.get_attribute(instance, 'nonexistens') is None
 
 
+def test_serializer_read_only_write_only_serialization():
+    class ExampleSerializer(BaseSerializer):
+        readonly = ExampleField('A read-only field', read_only=True)
+        writeonly = ExampleField('A write-only field', write_only=True)
+
+    serializer = ExampleSerializer()
+
+    assert serializer.to_representation(
+        {"writeonly": "foo", 'readonly': 'bar'}
+    ) == {"readonly": "bar"}
+
+
+@pytest.mark.xfail(
+    # note: this is forward compatibility test that will ensure the behaviour
+    #       will change in future major release.
+    graceful.VERSION[0] < 1,
+    reason="graceful<1.0.0 does not enforce validation on deserialization",
+    strict=True,
+)
+def test_serializer_read_only_write_only_deserialization():
+    class ExampleSerializer(BaseSerializer):
+        readonly = ExampleField('A read-only field', read_only=True)
+        writeonly = ExampleField('A write-only field', write_only=True)
+
+    serializer = ExampleSerializer()
+
+    serializer.from_representation({"writeonly": "x"}) == {"writeonly": "x"}
+
+    with pytest.raises(DeserializationError):
+        serializer.from_representation({"writeonly": "x", 'readonly': 'x'})
+
+    with pytest.raises(DeserializationError):
+        serializer.from_representation({'readonly': 'x'})
+
+
+def test_serializer_read_only_write_only_validation():
+    class ExampleSerializer(BaseSerializer):
+        readonly = ExampleField('A read-only field', read_only=True)
+        writeonly = ExampleField('A write-only field', write_only=True)
+
+    serializer = ExampleSerializer()
+    serializer.validate({"writeonly": "x"})
+
+    with pytest.raises(DeserializationError):
+        serializer.validate({"writeonly": "x", 'readonly': 'x'})
+
+    with pytest.raises(DeserializationError):
+        serializer.validate({'readonly': 'x'})
+
+
 def test_serializer_source_wildcard():
     """
     Test that '*' wildcard causes whole instance is returned on get attribute

pip install -e .
parse_log_pytest
requirements.txt
pytest
3.5
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
falcon==3.1.3
-e git+https://github.com/swistakm/graceful.git@d7679ac81be5e8c16f17467fe27efbaabc3ac4c8#egg=graceful
importlib-metadata==4.8.3
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
singledispatch==3.7.0
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: graceful
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
      - falcon==3.1.3
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - singledispatch==3.7.0
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/graceful

tests/test_fields.py::test_base_field_write_or_read_only_not_both tests/test_fields.py::test_base_field_describe tests/test_serializers.py::test_serializer_read_only_write_only_serialization tests/test_serializers.py::test_serializer_read_only_write_only_validation
tests/test_fields.py::test_base_field_implementation_hooks tests/test_fields.py::test_base_field_validate tests/test_fields.py::test_raw_field[int] tests/test_fields.py::test_raw_field[float] tests/test_fields.py::test_raw_field[str] tests/test_fields.py::test_raw_field[dict] tests/test_fields.py::test_raw_field[tuple] tests/test_fields.py::test_raw_field[set] tests/test_fields.py::test_string_field tests/test_fields.py::test_int_field tests/test_fields.py::test_bool_field tests/test_fields.py::test_bool_field_custom_representations tests/test_fields.py::test_float_field tests/test_serializers.py::test_simple_serializer_definition tests/test_serializers.py::test_empty_serializer_definition tests/test_serializers.py::test_serializer_inheritance tests/test_serializers.py::test_serializer_field_overriding tests/test_serializers.py::test_serialiser_simple_representation tests/test_serializers.py::test_serialiser_sources_representation tests/test_serializers.py::test_serializer_set_attribute tests/test_serializers.py::test_serializer_get_attribute tests/test_serializers.py::test_serializer_source_wildcard tests/test_serializers.py::test_serializer_source_field_with_wildcard tests/test_serializers.py::test_serializer_describe tests/test_serializers.py::test_serialiser_with_field_many tests/test_serializers.py::test_serializer_many_validation
docs/guide/serializers.rst src/graceful/fields.py src/graceful/serializers.py
docs/guide/serializers.rst src/graceful/fields.py src/graceful/serializers.py